[
  {
    "path": ".clang-format",
    "content": "# Copyright (c) 2010-2023 Belledonne Communications SARL.\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as\n# published by the Free Software Foundation, either version 3 of the\n# License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n---\nLanguage:        Cpp\nBasedOnStyle:  LLVM\nAccessModifierOffset: -4\nAllowShortFunctionsOnASingleLine: None\nAllowShortIfStatementsOnASingleLine: AllIfsAndElse\nAlwaysBreakTemplateDeclarations: Yes\nBinPackParameters: false\nColumnLimit:     120\nPointerAlignment: Right\nIndentCaseLabels: true\nIndentWidth:     4\nStandard:        c++14\nTabWidth:        4\nUseTab:          ForIndentation\n...\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "---\nname: Bug report\ndescription: File a bug/issue\ntitle: \"[Bug]: \"\nlabels: [\"bug\"]\n\nbody:\n- type: markdown\n  attributes:\n    value: '# Reminder'\n- type: markdown\n  attributes:\n    value: |\n            The responses are provided by the **community** and, on a **best effort** basis, by some Belledonne Communications SARL engineers working on Linphone and its related projects.\n            The community means any people all around the world simply willing to participate to the discussions.\n\n            Belledonne Communications SARL **disclaims any WARRANTY** that the content posted on github issues or mailing lists is technically correct.\n            Responses from Belledonne Communications SARL engineers shall be considered as individual contributions and shall not be seen as Belledonne Communications's official point of view or commitment.\n\n            The Github issue tracker must be seen as a place for **collaboration**. Issues submitted should be of general interest, in the goal of improving the software. Consider that a **well documented** issue (with precise reproduction procedure, logs, stack trace if relevant, possibly a corrective patch) has a higher chance to receive interest and feedback from community members and Belledonne Communications' engineers.\n\n            __Issues poorly documented, with no facts, or asking for debugging assistance for a custom app using Linphone's libraries, or for a modified version of Linphone are unlikely to receive any kind of response.__\n\n            People using Linphone or its related projects within the scope of their company job are invited to contact [Belledonne Communications](https://linphone.org/contact#content-bottom3) in order to obtain commercial support.\n\n- type: markdown\n  attributes:\n    value: |\n            # Well ordered issues are treated issues\n            **In our apps, the [Linphone-SDK](https://github.com/BelledonneCommunications/linphone-sdk) is used.**\n            Please report your issue here **ONLY** if you are sure that the origin of the error is in this module.\n            Otherwise, open an issue in the repository of the app you are using or in the Linphone-SDK, and we will move it to the related module.\n\n- type: markdown\n  attributes:\n    value: |\n            # Useful links\n            [Linphone.org](https://linphone.org)\n            [Linphone commercial contact](https://linphone.org/contact#content-bottom3)\n            Linphone Vulnerability/Security contact: vulnerabilities@linphone.org\n            [Contributor agreement (to sign and to return to sales@belledonne-communications.com for a pull request)](https://linphone.org/sites/default/files/bc-contributor-agreement_0.pdf)\n\n- type: textarea\n  attributes:\n    label: |\n            Context\n    description: |\n                  - For which purpose do you use the project ?\n                  - With which software/hardware it is integrated ?\n                  - Did you use sip.linphone.org or a different SIP service (in this case specify which one and which version) ?\n    value: |\n            I use this project in a custom app running on Linux with the sip.linphone.org service for my company. I want to do a simple call between an Android phone and a Linux client. There is an error with a method of this project and I'm sure that I followed the documentation and double checked before posting.\n  validations:\n    required: true\n\n- type: textarea\n  attributes:\n    label: General information\n    description: |\n                  Complete it multiple time if there are multiple devices involved.\n                  Please note that the issue has more chances to be read if you report a bug seen in the latest version of the module.\n\n                  Ex:\n                  - Device: [e.g. Samsung Note 20 Ultra]\n                  - OS: [e.g. Android 11]\n                  - Version of the App [e.g. 4.3.1]\n                  - Version of the SDK [e.g 4.4.16]\n    value: |\n                  - Device:\n                  - OS:\n                  - Version of the App:\n                  - Version of the SDK:\n  validations:\n    required: true\n\n- type: textarea\n  attributes:\n    label: Expected behaviour\n    description: \"A clear and concise description of what you expected to happen.\"\n    value: |\n            I wanted to do a simple call with the Linux client calling the Android phone. However, the phone doesn't ring when it is asleep.\n  validations:\n    required: true\n\n- type: textarea\n  attributes:\n    label: To Reproduce\n    description: \"Steps to reproduce the behavior:\"\n    value: |\n            1. Go to '...'\n            2. Click on '....'\n            3. Scroll down to '....'\n            4. See error\n  validations:\n    required: true\n\n- type: textarea\n  attributes:\n    label: 'Additional context'\n    value: Add any other context about the problem here.\n\n- type: markdown\n  attributes:\n    value: |\n            # Logs\n            ## Module logs\n            Enable debug logs in advanced section of the settings, restart the app, reproduce the issue and then go to About page, click on \"Send logs\" and copy/paste the link here.\n            If you doesn't have such an option, just provide the logs in attachments.\n\n- type: input\n  attributes:\n    label: 'SDK logs URL'\n\n- type: markdown\n  attributes:\n    value: |\n            ## Module crash logs\n            In case of a crash related to this module, please also provide the backtrace of the crash in attachments using adb logcat (Android) or the device console (iOS).\n            For desktop versions, you can get the backtrace from a core dump.\n\n- type: markdown\n  attributes:\n    value: |\n            # Screenshots\n            Please add screenshots in attachments to help us to understand your problem.\n\n- type: markdown\n  attributes:\n    value: |\n              # Pcap file\n              If this is a network issue, join a pcap file of your attempt in attachments (done with Wireshark or TCPDump, for example)\n"
  },
  {
    "path": ".gitignore",
    "content": "INSTALL\n*.o\n*.lo\n*.la\nconfigure\nMakefile.in\nmissing\nltmain.sh\nlibtool\nMakefile\n.libs\naclocal.m4\nconfig.guess\nconfig.status\nconfig.log\nconfig.sub\ndepcomp\n*~\nautom4te.cache/\ninstall-sh\nortp-config.h.in\nortp-config.h\nortp.spec\nortp.pc\nortp.doxygen\nlt*.m4\nlibtool.m4\ndoc\nortp.defs\nINSTALL\nstamp-h1\n*.vcxproj.*\ncompile\ngit-clang-format.diff\n*.DS_Store\n"
  },
  {
    "path": "AUTHORS.md",
    "content": "- Simon MORLAT (simon dot morlat at linphone dot org) is the original author the oRTP library.\n\n## Contributors:\n\n* Copyright (c) 2013 Vadim Zhukov <persgray@gmail.com>\n\nPermission to use, copy, modify, and distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n- support of arc4random()\n- bugfix in b64.c for platforms where char is unsigned.\n\n\n* Lovadina Nicola < lovadina dot nicola dot 10272 at unimo dot it > worked on RTCP support.\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [5.4.0] - 2025-03-11\n\n### Added\n- Flexible forward error correction (FEC) algorithm implemented according to https://datatracker.ietf.org/doc/html/rfc8627\n  The flexible mask is not implemented.\n- Added goog-remb RTCP message parser\n\n### Changed\n- RtpBundle logic to handle receiving of multiple RTP streams identified by SSRC under the same mid identifier\n- Optimisations and refactoring of RtpBundle for faster processing\n- RtpBundle can be used as well to send packets (in transfer mode)\n- Improve accuracy of upload bandwidth measurement\n\n\n## [5.3.0] - 2023-12-18\n\n### Added\n- Bandwidth estimator for audio streams - to get a rough estimate of available bandwidth.\n\n### Changed\n- reworked bandwidth estimator for video streams, to improve performance in case of video codecs that outputs small\n  sequences of packets per frame (H265, AV-1).\n\n### Removed\n- ortp memory functions (replaced by bctoolbox ones)\n- most of port.c content, that is replaced by bctoolbox\n\n\n## [5.1.0] - 2022-02-14\n\n### Added\n- New forward error correction (FEC) algorithm implemented according to https://datatracker.ietf.org/doc/html/rfc8627\n  Eperimental stage, work in progress.\n\n### Fixed\n- Standalone compilation (out of linphone-sdk).\n- Bundle mode warnings and few inconsistencies.\n\n\n## [5.0.0] - 2021-07-08\n\n### Fixed\n- small memory leak around TMMBR receiving.\n\n\n## [4.5.0] - 2021-03-29\n\n### Fixed\n- minor fixes\n\n## [4.4.0] - 2019-06-16\n\n### Added\n- RTP bundling according to https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54\n- RTP extension header support\n- IP_PKTINFO for outgoing packets - useful for ICE. This let specify the source IP address to use while sending a packet.\n\n### Changed\n- Version number aligned with other linphone-sdk components, for simplicity.\n\n### Fixed\n- Random crash when network simulator is activated, while destroying an RtpSession.\n\n\n\n## [1.1.0] - 2019-09-18\n\n### Added\n- Immediate NACK handling, to handle retransmission of lost packets.\n\n### Changed\n- License is now GNU GPLv3.\n\n\n\n## [1.0.0] - 2017-01-10\n\n### Added\n- new adaptive jitter buffer algorithm, with improved performance.\n\n### Changed\n- License is changed from LGPLv2 to GPLv2.\n\n\n\n## [0.27.0] - 2016-06-01\n\n### Changed\n- bctoolbox is added as dependency.\n\n### Fixed\n- DSCP handling on Windows.\n- IPv6 handling for Windows and Android.\n\n\n## [0.25.0] - 2015-11-02\n\n### Added\n- AVPF generic NACK\n- Payload type definitions for real time text and codec2.\n\n### Fixed\n- Various things.\n\n\n## [0.24.1] - 2015-05-31\n\n### Added\n- TMMBR and TMMBN handling (RFC5104).\n\n\n## [0.24.0] - 2015-03-11\n\n### Added\n- RTCP send algorithm as describe in RFC3550.\n- RTCP XR (RFC3611).\n- RTCP send algorithm for AVPF streams as described in RFC4585.\n\n\n\n## [0.23.0] - 2014-02-19\n\n### Changed\n- network simulator improvements.\n- updated to use ZRTPCPP>=4\n\n### Fixed\n- security issues.\n\t\n\n## [0.22.0] - 2012-05-27\n\n### Changed\n- Network simulator improvements for simulating random lost packets.\n\n### Fixed\n- SRTP initialization.\n\n\t\n## [0.19.0] - 2012-02-17\n\n### Added\n- ZRTP media encryption.\n\n\n## [0.18.0] - 2011-12-22\n\n### Added\n- SRTP media encryption\n\n\n## [0.17.0] - 2011-05-??\n\n### Added\n- rtp_session_get_round_trip_propagation()\n\n### Fixed\n- RTCP support.\n\n\n## [0.16.0] - 2010-05-10\n\n### Added\n- DSCP handling on Windows.\n- Accessors to struct PayloadType.\n- new payload type definitions.\n\n### Changed\n- update stun api to support new RFC.\n\n### Fixed\n- gcc warnings.\n\n\n## [0.15.0] - 2008-10-13\n\n### Changed\n- reduce number of memory allocation: !! attention here ABI/API change !!\n\t\tIf you are using mp=rtp_session_recvm_with_ts(), the payload data is no more pointed by mp->b_cont->b_rptr.\n\t\tInstead you can use the following to skip the header:\n\t\t\trtp_get_payload(mp,mp->b_rptr);\n\n### Fixed\n- telephone event presence detection bug.\n\n\n## [0.14.3] - 2008-03-14\n\n### Added\n- new ortp_set_memory_functions() method.\n\n### Changed\n- jitter buffer simplification and improvements\n\n\n## [0.14.0] - 2007-07-27\n\n### Added\n- Number of channels in PayloadType (interface changed !).\n- srtp optional support (using libsrtp from http://srtp.sf.net)\n\n### Changed\n- optimisations.\n\n\n## [0.13.1] - 2007-04-11\n\n### Changed\n- do not recv rtcp packets from rtp_session_sendm_with_ts() when session is not send-only.\n- removed gtk-doc, using doxygen instead.\n\n\n## [0.13.0] - 2007-01-23\n\n### Added\n- new telephone-event types.\n- pluggable transport layer.\n\n### Changed\n- enables use of different RtpProfile for send and recv directions.\n\n### Fixed\n- RTCP memory leak.\n\n\n## [0.12.0] - 2006-11-09\n\n### Added\n- enable 0 ms jitter buffer (implies permissive dequeuing of packets).\n- enable optional connected mode: the udp socket is connect()ed so only \n\t  packets coming from the connected destination are received.\n\n### Changed\n- jitter buffer accuracy improved.\n\n### Fixed\n- statistics.\n\n\n## [0.11.0] - 2006-08-22\n\n### Added\n- rtp_session_set_dscp(), rtp_session_send_rtcp_APP().\n\n### Fixed\n- statistics.\n\n\n## [0.10.0] - 2006-05-30\n\n### Added\n- new RTCP parser\n- new event api\n- stun helper routines\n- permissive algorithm for video packet enqueueing\n\n\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "############################################################################\n# Copyright (c) 2010-2023 Belledonne Communications SARL.\n#\n# This file is part of oRTP \n# (see https://gitlab.linphone.org/BC/public/ortp).\n#\n############################################################################\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as\n# published by the Free Software Foundation, either version 3 of the\n# License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. If not, see <http://www.gnu.org/licenses/>.\n############################################################################\n\ncmake_minimum_required(VERSION 3.22)\n\nproject(Ortp VERSION 5.5.0)\n\n\nset(ORTP_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})\nset(ORTP_MINOR_VERSION ${PROJECT_VERSION_MINOR})\nset(ORTP_MICRO_VERSION ${PROJECT_VERSION_PATCH})\nset(ORTP_VERSION ${PROJECT_VERSION})\nset(ORTP_SO_VERSION \"15\") # incremented for 4.4.0 version.\nset(ORTP_DOC_VERSION \"${ORTP_VERSION_MAJOR}.${ORTP_VERSION_MINOR}\")\n\n\noption(ENABLE_DOC \"Enable documentation generation with Doxygen.\" YES)\noption(ENABLE_NTP_TIMESTAMP \"Turn on NTP timestamping on packet reception.\" NO)\noption(ENABLE_PERF \"Disable costly features to reduce cpu consumtion and increase performance.\" NO)\noption(ENABLE_STRICT \"Build with strict compile options.\" YES)\noption(ENABLE_TESTS \"Enable compilation of test programs.\" NO)\noption(ENABLE_UNIT_TESTS \"Enable compilation of unit tests.\" YES)\noption(ENABLE_DEBUG_LOGS \"Turn on or off debug level logs.\" NO)\noption(ENABLE_PACKAGE_SOURCE \"Create 'package_source' target for source archive making\" OFF)\nset(WITH_THREAD_STACK_SIZE \"0\" CACHE STRING \"Set thread stack size (0 is the OS default).\")\n\n\nlist(APPEND CMAKE_MODULE_PATH \"${PROJECT_SOURCE_DIR}/cmake\")\n\n\ninclude(CheckIncludeFile)\ninclude(CheckFunctionExists)\ninclude(GNUInstallDirs)\ninclude(CheckCSourceCompiles)\ninclude(CheckCXXSourceCompiles)\ninclude(CheckSymbolExists)\n\nif(NOT CMAKE_INSTALL_RPATH AND CMAKE_INSTALL_PREFIX)\n\tset(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR})\n\tmessage(STATUS \"Setting install rpath to ${CMAKE_INSTALL_RPATH}\")\nendif()\n\nfind_package(Threads)\nfind_library(LIBM NAMES m)\n\nfind_package(BCToolbox 5.3.0 REQUIRED)\n#Mandatory to init default compilation flags\nbc_init_compilation_flags(STRICT_OPTIONS_CPP STRICT_OPTIONS_C STRICT_OPTIONS_CXX ENABLE_STRICT)\n\ncheck_include_file(sys/uio.h HAVE_SYS_UIO_H)\ncheck_include_file(sys/audio.h HAVE_SYS_AUDIO_H)\nif(NOT ANDROID)\n\tcheck_include_file(sys/shm.h HAVE_SYS_SHM_H)\nendif()\n\nset (CMAKE_CXX_STANDARD 17)\n\nset(CMAKE_CXX_FLAGS_BAK \"${CMAKE_CXX_FLAGS}\")\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++17\")# need this for check_cxx_source_compiles because CMAKE_CXX_STANDARD doesn't work.\ncheck_cxx_source_compiles(\"#include <atomic>\nusing namespace std;\nint main(int argc, char *argv[]) {\natomic_int current_ref;\natomic_init(&current_ref, 1);\natomic_int previous_ref(atomic_fetch_sub_explicit(&current_ref, 1, memory_order_release));\nreturn 0;\n}\"\n\tHAVE_ATOMIC)\nif(NOT HAVE_ATOMIC)\n\tmessage(FATAL_ERROR \"Atomic(C++) libraries have not been found for ORTP.\")\nendif()\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS_BAK}\")\n\ncheck_function_exists(arc4random HAVE_ARC4RANDOM)\ncheck_symbol_exists(recvmsg \"sys/socket.h\" HAVE_RECVMSG)\ncheck_symbol_exists(sendmsg \"sys/socket.h\" HAVE_SENDMSG)\n\ninclude(TestBigEndian)\ntest_big_endian(WORDS_BIGENDIAN)\nif(WORDS_BIGENDIAN)\n\tset(ORTP_BIGENDIAN 1)\nendif()\n\n\ninclude_directories(\n\tinclude/\n\tsrc/\n\t${PROJECT_BINARY_DIR}\n)\n\n\nset(ORTP_CPPFLAGS)\nif(ENABLE_PERF)\n\tset(PERF 1)\nendif()\nif(ENABLE_NTP_TIMESTAMP)\n\tset(ORTP_TIMESTAMP 1)\n\tlist(APPEND ORTP_CPPFLAGS \"-DORTP_TIMESTAMP\")\nendif()\nif(ENABLE_DEBUG_LOGS)\n\tadd_definitions(\"-DORTP_DEBUG_MODE -DBCTBX_DEBUG_MODE\")\nendif()\nif(CMAKE_USE_PTHREADS_INIT)\n\tset(ORTP_DEFAULT_THREAD_STACK_SIZE ${WITH_THREAD_STACK_SIZE})\nendif()\nif(APPLE)\n\tset(__APPLE_USE_RFC_3542 1)\nendif()\nif(ORTP_CPPFLAGS)\n\tadd_definitions(${ORTP_CPPFLAGS})\nendif()\nset(POSIXTIMER_INTERVAL 10000)\nconfigure_file(${PROJECT_SOURCE_DIR}/ortp-config.h.cmake ${PROJECT_BINARY_DIR}/ortp-config.h)\nset_source_files_properties(${PROJECT_BINARY_DIR}/ortp-config.h PROPERTIES GENERATED ON)\nadd_compile_definitions(\"HAVE_CONFIG_H\")\n\n# Enable stdint.h limit macros on C++ files. (Windows only.)\nif(MSVC)\n\tadd_compile_definitions(\"__STDC_LIMIT_MACROS\")\nendif()\n\nif(MSVC)\n\tif(ENABLE_STRICT)\n\t\tlist(APPEND STRICT_OPTIONS_CPP \"/WX\")\n\tendif()\n    add_definitions(-D_CRT_SECURE_NO_WARNINGS)\nendif()\nif(STRICT_OPTIONS_CPP)\n\tlist(REMOVE_DUPLICATES STRICT_OPTIONS_CPP)\nendif()\nif(STRICT_OPTIONS_C)\n\tlist(REMOVE_DUPLICATES STRICT_OPTIONS_C)\nendif()\n\nadd_subdirectory(include)\nadd_subdirectory(src)\nif(ENABLE_UNIT_TESTS AND NOT WIN32)\n\tadd_subdirectory(tester)\nendif()\n\nif(ENABLE_DOC)\n\tfind_package(Doxygen)\n\tif(DOXYGEN_FOUND)\n\t\tset(DOXYGEN_INPUT \"\")\n\t\tfile(GLOB DOC_INPUT_FILES\n\t\t\t\"${PROJECT_SOURCE_DIR}/include/ortp/[^.]*.h\"\n\t\t\t\"${PROJECT_SOURCE_DIR}/src/[^.]*.h\"\n\t\t\t\"${PROJECT_SOURCE_DIR}/src/[^.]*.c\"\n\t\t)\n\t\tforeach (INPUT_FILE ${DOC_INPUT_FILES})\n\t\t\tstring(CONCAT DOXYGEN_INPUT ${DOXYGEN_INPUT} \" \\\"${INPUT_FILE}\\\"\")\n\t\tendforeach ()\n\t\tconfigure_file(${PROJECT_SOURCE_DIR}/ortp.doxygen.in ${PROJECT_BINARY_DIR}/ortp.doxygen)\n\t\tadd_custom_command(OUTPUT \"${PROJECT_BINARY_DIR}/doc/html/index.html\"\n\t\t\tCOMMAND \"${DOXYGEN_EXECUTABLE}\" \"${PROJECT_BINARY_DIR}/ortp.doxygen\"\n\t\t\tDEPENDS \"${PROJECT_BINARY_DIR}/ortp.doxygen\" ${DOC_INPUT_FILES}\n\t\t)\n\t\tadd_custom_target(ortp-html-doc ALL DEPENDS \"${PROJECT_BINARY_DIR}/doc/html/index.html\")\n\t\tinstall(DIRECTORY \"${PROJECT_BINARY_DIR}/doc/html\"\n\t\t\tDESTINATION \"${CMAKE_INSTALL_DATADIR}/doc/ortp-${ORTP_VERSION}\")\n\tendif()\nendif()\n\n\n\nset(prefix ${CMAKE_INSTALL_PREFIX})\nset(exec_prefix ${prefix}/bin)\nset(libdir ${prefix}/lib)\nset(includedir ${prefix}/include)\nset(ORTP_PKGCONFIG_VERSION \"${ORTP_VERSION}\")\nset(ORTPDEPS_LIBS )\n\nconfigure_file(${PROJECT_SOURCE_DIR}/ortp.pc.in ${PROJECT_BINARY_DIR}/ortp.pc)\ninstall(FILES ${PROJECT_BINARY_DIR}/ortp.pc DESTINATION \"${CMAKE_INSTALL_LIBDIR}/pkgconfig\")\n\nif (ENABLE_PACKAGE_SOURCE)\n\tadd_subdirectory(build)\nendif()\n\ninclude(CMakePackageConfigHelpers)\nset(CMAKE_MODULES_INSTALL_DIR \"${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake\")\nconfigure_package_config_file(\"cmake/${PROJECT_NAME}Config.cmake.in\" \"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake\"\n\tINSTALL_DESTINATION \"${CMAKE_MODULES_INSTALL_DIR}\"\n\tNO_SET_AND_CHECK_MACRO\n)\nwrite_basic_package_version_file(\"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake\"\n\tVERSION ${PROJECT_VERSION}\n\tCOMPATIBILITY AnyNewerVersion\n)\ninstall(FILES\n\t\"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake\"\n\t\"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake\"\n\tDESTINATION ${CMAKE_MODULES_INSTALL_DIR}\n)\n\ninstall(EXPORT ${PROJECT_NAME}Targets\n\tFILE \"${PROJECT_NAME}Targets.cmake\"\n\tDESTINATION ${CMAKE_MODULES_INSTALL_DIR}\n)\n\ninstall(FILES \"${PROJECT_SOURCE_DIR}/README.md\" \n\t\"${PROJECT_SOURCE_DIR}/CHANGELOG.md\" \n\t\"${PROJECT_SOURCE_DIR}/LICENSE.txt\" \n\t\"${PROJECT_SOURCE_DIR}/AUTHORS.md\" \n\tDESTINATION \"${CMAKE_INSTALL_DATADIR}/doc/ortp-${ORTP_VERSION}\"\n)\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<https://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "Makefile.am",
    "content": "# linphone/oRTP/Makefile.am -- \n\nACLOCAL_AMFLAGS = -I m4 $(ACLOCAL_MACOS_FLAGS)\n\nEXTRA_DIST = \\\n\toRTP.prj \\\n\tortp-config.h.in \\\n\tpkg.list autogen.sh \\\n\tortp.pc.in \\\n\tortp.spec.in \\\n\tortp.spec \\\n\tortp.doxygen \\\n\toRTP.pws \\\n\tCMakeLists.txt \\\n\tinclude/CMakeLists.txt \\\n\tsrc/CMakeLists.txt \\\n\tsrc/tests/CMakeLists.txt \\\n\tORTPConfig.cmake.in \\\n\tortp-config.h.cmake \\\n\tREADME.md\n\n\nSUBDIRS=src build m4 include\n\nACLOCAL_FLAGS=-I$(top_srcdir)/m4\n\npkgconfigdir = $(libdir)/pkgconfig\npkgconfig_DATA = ortp.pc\n\nSOURCES=$(top_srcdir)/include/ortp/*.h $(top_srcdir)/src/*.c $(top_srcdir)/src/*.h\n\nCLEANFILES=\n\n#html doc\nif HAVE_DOXYGEN\n\n# docdir & pkgdocdir are not always defined by automake\npkgdocdir=$(docdir)/$(PACKAGE)-$(VERSION)\ndoc_htmldir=$(pkgdocdir)/html\n\npkgdoc_DATA = README.md AUTHORS ChangeLog COPYING\ndoc_html_DATA = $(top_builddir)/doc/html/html.tar\n\n$(top_builddir)/doc/html/html.tar: $(top_builddir)/doc/html/index.html\n\tcd $(top_builddir)/doc/html/ && rm -f html.tar && tar cf html.tar *\n\n$(top_builddir)/doc/html/index.html: $(SOURCES) ortp.doxygen Makefile.am\n\trm -rf doc\n\t$(DOXYGEN) ortp.doxygen\n\ninstall-data-hook:\n\tcd $(DESTDIR)$(doc_htmldir) && tar xf html.tar && rm -f html.tar\n\nuninstall-hook:\n\t- cd $(DESTDIR)$(doc_htmldir) && rm -f *\n\n\nendif\n\n## oRTP packaging methods:\n\n# `make rpm'\n\n.phony: rpm\n\nrpm:\n\t$(MAKE) dist\n# <https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=206841>\n\tTAR_OPTIONS=--wildcards rpmbuild -ta --clean --rmsource --rmspec $(PACKAGE)-$(VERSION).tar.gz\n\n# `make package'\n\nif WITH_EPM\n\n.PHONY: package\n\nPKG_NAME=$(PACKAGE)-$(VERSION)-$(RELEASE)\nBUILDROOT=`pwd`/epm-install\n\npackage: $(srcdir)/pkg.list $(srcdir)/configure\n\t-rm -rf pkg $(BUILDROOT) $(PKG_NAME).*\n\t$(MAKE) install DESTDIR=$(BUILDROOT)\n\t$(MKEPMLIST) -u $(SYS_USER) -g $(SYS_GROUP) --prefix $(prefix) \\\n\t\t$(BUILDROOT)/$(prefix) > files.list\n\t$(EPM) -vv -f native -g -n -a $(ARCH) --keep-files --output-dir pkg \\\n\t\tsrcdir=$(srcdir) \\\n\t\ttop_srcdir=$(top_srcdir) \\\n\t\ttop_builddir=$(top_builddir) \\\n\t\tPACKAGE=$(PACKAGE) \\\n\t\tSUMMARY=\"$(SUMMARY)\" \\\n\t\tVERSION=$(ORTP_PKGCONFIG_VERSION) \\\n\t\tRELEASE=$(RELEASE) \\\n\t\tLICENSE=\"$(LICENSE)\" \\\n\t\tVENDOR=\"$(VENDOR)\" \\\n\t\tPACKAGER=\"$(PACKAGER)\" \\\n\t\t$(PACKAGE) $(srcdir)/pkg.list\n\tmv -f pkg/$(PACKAGE)-$(ORTP_PKGCONFIG_VERSION)-$(RELEASE).$(EPM_PKG_EXT) $(PKG_NAME).$(ARCH).$(EPM_PKG_EXT)\n\nclean-local:\n\trm -rf pkg $(BUILDROOT)\n\trm -f files.list\n\trm -rf doc\n\nendif WITH_EPM\n\ndistclean-local:\n\t-rm -f ortp.defs\n\t-rm -rf doc\n\nall-local: ortp.spec\n\nortp.spec: ortp.spec.in\n\n\ndeb:\n\t$(MAKE) dist\n\tmv $(distdir).tar.gz ../$(PACKAGE)_$(VERSION).orig.tar.gz\n\tdpkg-buildpackage -us -uc\n"
  },
  {
    "path": "README.md",
    "content": "**CAUTION: this git repository is no longer updated. The project has been merged into the linphone-sdk ([Gitlab](https://gitlab.linphone.org/BC/public/linphone-sdk), [Github](https://github.com/BelledonneCommunications/linphone-sdk)) git repository and will continue his life there.**\n\n**Versions up to 5.4 (included) are still kept into this repository, that will remain active on release/5.4 branch until the end of life of release 5.4.**\n\n[![pipeline status](https://gitlab.linphone.org/BC/public/ortp/badges/master/pipeline.svg)](https://gitlab.linphone.org/BC/public/ortp/commits/master)\n\noRTP\n====\n\n\noRTP is a C library implementing the RTP protocol (rfc3550). It is available\nfor most unix clones (primilarly Linux and HP-UX), and Microsoft Windows.\n\nFor additional information, please [visit oRTP's homepage on **linphone.org**](http://www.linphone.org/technical-corner/ortp).\n\n\nLicense\n-------\n\nCopyright © Belledonne Communications\n\noRTP is dual licensed, and is available either :\n\n - under a [GNU/AGPLv3 license](https://www.gnu.org/licenses/agpl-3.0.html), for free (open source). Please make sure that you understand and agree with the terms of this license before using it (see LICENSE.txt file for details).\n\n - under a proprietary license, for a fee, to be used in closed source applications. Contact [Belledonne Communications](https://www.linphone.org/contact) for any question about costs and services.\n\nPrior to version 1.0.0, oRTP was licensed under LGPLv2. Due to inclusion of new code licensed under GPLv2, oRTP has become GPLv2,\nand later in version 1.1.0, GPLv3.\nFor the sake of clarity, all source files headers were updated to mention the GPLv3 only.\noRTP versions prior to 1.0.0 of course remain LGPLv2.\n\n\nDependencies\n------------\n\n*bctoolbox[1]*: portability layer\n\n\nCompilation\n-----------\n\nAutotools procedure is deprecated. Use CMake to configure the source code.\n\n\tcmake . -DCMAKE_INSTALL_PREFIX=<prefix> -DCMAKE_PREFIX_PATH=<search_paths>\n\t\n\tmake\n\tmake install\n\n### Options:\n\n- `CMAKE_INSTALL_PREFIX=<string>` : install prefix\n- `CMAKE_PREFIX_PATH=<string>`    : column-separated list of prefixes where to search for dependencies\n- `ENABLE_TESTS=YES`              : build tester binaries\n- `ENABLE_DOC=NO`                 : do not generate the documentation\n- `ENABLE_DEBUG_LOGS=YES`         : turn on debug-level logs\n\n\n### Note for packagers:\n\nOur CMake scripts may automatically add some paths into research paths of generated binaries.\nTo ensure that the installed binaries are striped of any rpath, use `-DCMAKE_SKIP_INSTALL_RPATH=ON`\nwhile you invoke cmake.\n\nRpm packaging\nortp rpm can be generated with cmake3 using the following command:\nmkdir WORK\ncd WORK\ncmake3 ../\nmake package_source\nrpmbuild -ta --clean --rmsource --rmspec ortp-<version>-<release>.tar.gz\n\n\nHow do you I test ?\n-------------------\n\nThere are shorts and easy to understand programs given with the library. There are good example\nto understand how to use oRTP api.\n\n- rtpsend : sends a stream from a file on disk.\n- rtprecv : receives a stream and writes it to disk.\n- mrtpsend: sends multiple streams from a file on disk to a range of remote port.\n- mrtprecv:\treceives mutiple streams on a range of local ports and writes them on disk.\n\n\nIs there some documentation ?\n-----------------------------\n\nSee the doxygen generated API documentation in docs/html. Program examples are a very good\nstarting point.\n\n\n\n----------------------------------------\n\n\n[1] http://www.linphone.org/releases/sources/bctoolbox\n"
  },
  {
    "path": "autogen.sh",
    "content": "#!/bin/sh\n##\n## Copyright (c) 2010-2022 Belledonne Communications SARL.\n##\n## This file is part of oRTP \n## (see https://gitlab.linphone.org/BC/public/ortp).\n##\n## This program is free software: you can redistribute it and/or modify\n## it under the terms of the GNU Affero General Public License as\n## published by the Free Software Foundation, either version 3 of the\n## License, or (at your option) any later version.\n##\n## This program is distributed in the hope that it will be useful,\n## but WITHOUT ANY WARRANTY; without even the implied warranty of\n## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n## GNU Affero General Public License for more details.\n##\n## You should have received a copy of the GNU Affero General Public License\n## along with this program. If not, see <http://www.gnu.org/licenses/>.\n##\n\nsrcdir=`dirname $0`\ntest -z \"$srcdir\" && srcdir=.\n\nTHEDIR=`pwd`\ncd $srcdir\n\n#AM_VERSION=\"1.10\"\nif ! type aclocal-$AM_VERSION 1>/dev/null 2>&1; then\n\t# automake-1.10 (recommended) is not available on Fedora 8\n\tAUTOMAKE=automake\n\tACLOCAL=aclocal\nelse\n\tACLOCAL=aclocal-${AM_VERSION}\n\tAUTOMAKE=automake-${AM_VERSION}\nfi\n\nlibtoolize=\"libtoolize\"\nfor lt in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do\n        if test -x /usr/bin/$lt ; then\n                libtoolize=$lt ; break\n        fi\n        if test -x /usr/local/bin/$lt ; then\n                libtoolize=$lt ; break\n        fi\n        if test -x /opt/local/bin/$lt ; then\n                libtoolize=$lt ; break\n        fi\ndone\n\nif test -d /usr/local/share/aclocal ; then\n\tACLOCAL_ARGS=\"$ACLOCAL_ARGS -I /usr/local/share/aclocal\"\nfi\n\nif test -d /share/aclocal ; then\n        ACLOCAL_ARGS=\"$ACLOCAL_ARGS -I /share/aclocal\"\nfi\n\nset -x\nrm -rf config.cache autom4te.cache\n$libtoolize --copy --force\n$ACLOCAL -I m4 $ACLOCAL_ARGS\nautoheader\n$AUTOMAKE --force-missing --add-missing --copy\nautoconf\n\n#install git pre-commit hooks if possible\nif [ -d .git/hooks ] && [ ! -f .git/hooks/pre-commit ]; then\n        cp .git-pre-commit .git/hooks/pre-commit\n        chmod +x .git/hooks/pre-commit\nfi\n\ncd $THEDIR\n"
  },
  {
    "path": "build/.gitignore",
    "content": "Makefile\nMakefile.in\n"
  },
  {
    "path": "build/CMakeLists.txt",
    "content": "############################################################################\n# Copyright (c) 2010-2023 Belledonne Communications SARL.\n#\n# This file is part of oRTP \n# (see https://gitlab.linphone.org/BC/public/ortp).\n#\n############################################################################\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as\n# published by the Free Software Foundation, either version 3 of the\n# License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. If not, see <http://www.gnu.org/licenses/>.\n############################################################################\n\nif(NOT CPACK_PACKAGE_NAME)\n\tset(CPACK_PACKAGE_NAME \"ortp\")\nendif()\n\nset(CPACK_SOURCE_IGNORE_FILES \"./.*\")\n\nbc_make_package_source_target()\n"
  },
  {
    "path": "build/Makefile.am",
    "content": "EXTRA_DIST=android/Android.mk android/ortp_AndroidConfig.h wp8/oRTP/oRTP.sln wp8/oRTP/inttypes.h wp8/oRTP/oRTP.vcxproj wp8/oRTP/stdint.h\n\n"
  },
  {
    "path": "build/android/Android.mk",
    "content": "##\n## Android.mk -Android build script-\n##\n##\n## Copyright (C) 2010  Belledonne Communications, Grenoble, France\n##\n##  This program is free software; you can redistribute it and/or modify\n##  it under the terms of the GNU General Public License as published by\n##  the Free Software Foundation; either version 2 of the License, or\n##  (at your option) any later version.\n##\n##  This program is distributed in the hope that it will be useful,\n##  but WITHOUT ANY WARRANTY; without even the implied warranty of\n##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n##  GNU Library General Public License for more details.\n##\n##  You should have received a copy of the GNU General Public License\n##  along with this program; if not, write to the Free Software\n##  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n##\n\n\nLOCAL_PATH:= $(call my-dir)/../../\ninclude $(CLEAR_VARS)\n\nLOCAL_MODULE := libortp\n\n\nLOCAL_SRC_FILES := \\\n\tsrc/avprofile.c \\\n\tsrc/b64.c \\\n\tsrc/congestiondetector.c \\\n\tsrc/event.c \\\n\tsrc/extremum.c \\\n\tsrc/jitterctl.c \\\n\tsrc/kalmanrls.c \\\n\tsrc/logging.c \\\n\tsrc/netsim.c \\\n\tsrc/ortp.c \\\n\tsrc/payloadtype.c \\\n\tsrc/port.c \\\n\tsrc/posixtimer.c \\\n\tsrc/rtcp.c \\\n\tsrc/rtcp_fb.c \\\n\tsrc/rtcp_xr.c \\\n\tsrc/rtcpparse.c \\\n\tsrc/rtpparse.c \\\n\tsrc/rtpprofile.c \\\n\tsrc/rtpsession.c \\\n\tsrc/rtpsession_inet.c \\\n\tsrc/rtpsignaltable.c  \\\n\tsrc/rtptimer.c \\\n\tsrc/scheduler.c \\\n\tsrc/sessionset.c \\\n\tsrc/str_utils.c\t\\\n\tsrc/telephonyevents.c \\\n\tsrc/utils.c\n\nLOCAL_CFLAGS += \\\n\t-DORTP_INET6 \\\n\t-UHAVE_CONFIG_H \\\n\t-include ortp_AndroidConfig.h \\\n\t-DHAVE_PTHREADS \\\n\t-Werror -Wall -Wno-error=strict-aliasing -Wuninitialized\n\n\nLOCAL_C_INCLUDES += \\\n\t$(LOCAL_PATH) \\\n\t$(LOCAL_PATH)/include \\\n\t$(LOCAL_PATH)/build/android\n\nLOCAL_CPPFLAGS = $(LOCAL_CLFAGS)\nLOCAL_CFLAGS += -Wdeclaration-after-statement\n\ninclude $(BUILD_STATIC_LIBRARY)\n"
  },
  {
    "path": "build/android/ortp_AndroidConfig.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n/* ortp-config.h.  Generated from ortp-config.h.in by configure.  */\n/* ortp-config.h.in.  Generated from configure.ac by autoheader.  */\n\n/* Defined when memory leak checking if enabled */\n/* #undef ENABLE_MEMCHECK */\n\n/* Define to 1 if you have the <dlfcn.h> header file. */\n#define HAVE_DLFCN_H 1\n\n/* Define to 1 if you have the <fcntl.h> header file. */\n#define HAVE_FCNTL_H 1\n\n/* Define to 1 if you have the <inttypes.h> header file. */\n#define HAVE_INTTYPES_H 1\n\n/* Define to 1 if you have the <linux/soundcard.h> header file. */\n#define HAVE_LINUX_SOUNDCARD_H 1\n\n/* Define to 1 if you have the <memory.h> header file. */\n#define HAVE_MEMORY_H 1\n\n/* Define to 1 if you have the <poll.h> header file. */\n#define HAVE_POLL_H 1\n\n/* Define to 1 if you have the `select' function. */\n#define HAVE_SELECT 1\n\n/* Define to 1 if you have the `seteuid' function. */\n#define HAVE_SETEUID 1\n\n/* Define to 1 if you have the `socket' function. */\n#define HAVE_SOCKET 1\n\n/* Defined when srtp support is compiled */\n/*#define HAVE_SRTP 1*/\n\n/* Define to 1 if you have the <stdint.h> header file. */\n#define HAVE_STDINT_H 1\n\n/* Define to 1 if you have the <stdlib.h> header file. */\n#define HAVE_STDLIB_H 1\n\n/* Define to 1 if you have the `strerror' function. */\n#define HAVE_STRERROR 1\n\n/* Define to 1 if you have the <strings.h> header file. */\n#define HAVE_STRINGS_H 1\n\n/* Define to 1 if you have the <string.h> header file. */\n#define HAVE_STRING_H 1\n\n/* Define to 1 if you have the <sys/audio.h> header file. */\n/* #undef HAVE_SYS_AUDIO_H */\n\n/* Define to 1 if you have the <sys/poll.h> header file. */\n#define HAVE_SYS_POLL_H 1\n\n/* Define to 1 if you have the <sys/stat.h> header file. */\n#define HAVE_SYS_STAT_H 1\n\n/* Define to 1 if you have the <sys/time.h> header file. */\n#define HAVE_SYS_TIME_H 1\n\n/* Define to 1 if you have the <sys/types.h> header file. */\n#define HAVE_SYS_TYPES_H 1\n\n/* Define to 1 if you have the <unistd.h> header file. */\n#define HAVE_UNISTD_H 1\n\n/* Defined if we should not use connect() on udp sockets */\n/* #undef NOCONNECT */\n\n/* Default thread stack size (0 = let operating system decide) */\n#define ORTP_DEFAULT_THREAD_STACK_SIZE 0\n\n/* major version */\n#define ORTP_MAJOR_VERSION 0\n\n/* micro version */\n#define ORTP_MICRO_VERSION 0\n\n/* minor version */\n#define ORTP_MINOR_VERSION 15\n\n/* ortp version number */\n#define ORTP_VERSION \"0.15.0\"\n\n/* Name of package */\n#define PACKAGE \"ortp\"\n\n/* Define to the address where bug reports for this package should be sent. */\n#define PACKAGE_BUGREPORT \"\"\n\n/* Define to the full name of this package. */\n#define PACKAGE_NAME \"ortp\"\n\n/* Define to the full name and version of this package. */\n#define PACKAGE_STRING \"ortp 0.15.0\"\n\n/* Define to the one symbol short name of this package. */\n#define PACKAGE_TARNAME \"ortp\"\n\n/* Define to the version of this package. */\n#define PACKAGE_VERSION \"0.15.0\"\n\n/* Defines the periodicity of the rtp scheduler in microseconds */\n#define POSIXTIMER_INTERVAL 10000\n\n/* Define to 1 if you have the ANSI C header files. */\n#define STDC_HEADERS 1\n\n/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */\n#define TIME_WITH_SYS_TIME 1\n\n/* Version number of package */\n#define VERSION \"0.15.0\"\n\n/* Define to 1 if your processor stores words with the most significant byte\n   first (like Motorola and SPARC, unlike Intel and VAX). */\n/* #undef WORDS_BIGENDIAN */\n\n/* Define to empty if `const' does not conform to ANSI C. */\n/* #undef const */\n\n/* Define to `__inline__' or `__inline' if that's what the C compiler\n   calls it, or to nothing if 'inline' is not supported under any name.  */\n#ifndef __cplusplus\n/* #undef inline */\n#endif\n\n/* /dev/random cannot be guessed at ./configure time in case or\n * cross-compilation */\n#define HAVE_DEV_RANDOM 1\n"
  },
  {
    "path": "build/osx/Info.plist.in",
    "content": "\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>English</string>\n\t<key>CFBundleExecutable</key>\n\t<string>ortp</string>\n\t<key>CFBundleGetInfoString</key>\n\t<string>${MACOSX_BUNDLE_INFO_STRING}</string>\n\t<key>CFBundleIconFile</key>\n\t<string>${MACOSX_BUNDLE_ICON_FILE}</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>${MACOSX_FRAMEWORK_IDENTIFIER}</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>\n\t<key>MinimumOSVersion</key>\n\t<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleLongVersionString</key>\n\t<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>\n\t<key>CFBundleName</key>\n\t<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>\n\t<key>CFBundlePackageType</key>\n\t<string>FMWK</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>${ORTP_VERSION}</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>${ORTP_VERSION}</string>\n\t<key>CSResourcesFileMapped</key>\n\t<true/>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>${MACOSX_BUNDLE_COPYRIGHT}</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n\t<key>NSHighResolutionCapable</key>\n\t<string>True</string>\n</dict>\n</plist>"
  },
  {
    "path": "build/rpm/ortp.spec.cmake",
    "content": "# -*- rpm-spec -*-\n#\n# ortp -- Real-time Transport Protocol Stack\n#\n# Default is optimized for Pentium IV but will execute on Pentium II &\n# later (i686).\n\n%define _prefix    @CMAKE_INSTALL_PREFIX@\n%define pkg_prefix @BC_PACKAGE_NAME_PREFIX@\n%define package_name @CPACK_PACKAGE_NAME@-${FULL_VERSION}\n\n# re-define some directories for older RPMBuild versions which don't. This messes up the doc/ dir\n# taken from https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros\n%define _datarootdir       %{_prefix}/share\n%define _datadir           %{_datarootdir}\n%define _docdir            %{_datadir}/doc\n\n%ifarch %ix86\n%define\t\tortp_cpu\tpentium4\n%endif\nSummary:\tReal-time Transport Protocol Stack\nName:\t\t@CPACK_PACKAGE_NAME@\nVersion:\t${RPM_VERSION}\nRelease:\t${RPM_RELEASE}%{?dist}\n#to be alined with redhat which changed epoc to 1 for an unknown reason\nEpoch:\t\t1\nLicense:\tGPL\nGroup:\t\tApplications/Communications\nURL:\t\thttp://linphone.org/ortp/\nSource0:\t%{package_name}.tar.gz\nBuildRoot:\t%{_tmppath}/%{name}-%{version}-%{release}-buildroot\n%ifarch %ix86\nBuildArch:\ti686\n%endif\n\nRequires:\t%{pkg_prefix}bctoolbox\n\n%if 0%{?rhel} && 0%{?rhel} <= 7\n%global cmake_name cmake3\n%define ctest_name ctest3\n%else\n%global cmake_name cmake\n%define ctest_name ctest\n%endif\n\n%description\noRTP is a GPL licensed C library implementing the RTP protocol\n(rfc3550). It is available for most unix clones (primilarly Linux and\nHP-UX), and Microsoft Windows.\n\n%package        devel\nSummary:        Headers, libraries and docs for the oRTP library\nGroup:          Development/Libraries\nBuildRequires:\tdoxygen\n#to be alined with redhat which changed epoc to 1 for an unknown reason\nEpoch:\t\t1\nRequires:      %{name} = %{epoch}:%{version}-%{release}\n\n%description    devel\noRTP is a GPL licensed C library implementing the RTP protocol\n(rfc1889). It is available for most unix clones (primilarly Linux and\nHP-UX), and Microsoft Windows.\n\nThis package contains header files and development libraries needed to\ndevelop programs using the oRTP library.\n\n%ifarch %ix86\n%define\tortp_arch_cflags -malign-double -march=i686 -mtune=%{ortp_cpu}\n%else\n# Must be non-empty\n%define ortp_arch_cflags -Wall\n%endif\n%define ortp_cflags %ortp_arch_cflags -Wall -g -pipe -pthread -O3 -fomit-frame-pointer -fno-schedule-insns -fschedule-insns2 -fno-strict-aliasing\n\n# This is for debian builds where debug_package has to be manually specified, whereas in centos it does not\n%define custom_debug_package %{!?_enable_debug_packages:%debug_package}%{?_enable_debug_package:%{nil}}\n%custom_debug_package\n\n%prep\n%setup -n %{package_name}\n\n%build\n%{expand:%%%cmake_name} . -DCMAKE_BUILD_TYPE=@CMAKE_BUILD_TYPE@ -DCMAKE_PREFIX_PATH:PATH=%{_prefix} @RPM_ALL_CMAKE_OPTIONS@\nmake %{?_smp_mflags}\n\n%install\nmake install DESTDIR=%{buildroot}\n\n# Dirty workaround to give exec rights for all shared libraries. Debian packaging needs this\n# TODO : set CMAKE_INSTALL_SO_NO_EXE for a cleaner workaround\nchmod +x `find %{buildroot} *.so.*`\n\n\n%check\n%{ctest_name} -V %{?_smp_mflags}\n\n%clean\nrm -rf $RPM_BUILD_ROOT\n\n%files\n%defattr(-,root,root,-)\n%doc %{_docdir}/ortp-@ORTP_DOC_VERSION@/README.md\n%doc %{_docdir}/ortp-@ORTP_DOC_VERSION@/CHANGELOG.md\n%doc %{_docdir}/ortp-@ORTP_DOC_VERSION@/LICENSE.txt\n%doc %{_docdir}/ortp-@ORTP_DOC_VERSION@/AUTHORS.md\n%{_libdir}/*.so.*\n\n%files devel\n%defattr(-,root,root,-)\n%if @ENABLE_DOC@\n%doc %{_docdir}/ortp-%{version}/html/*\n%endif\n%if @ENABLE_STATIC@\n%{_libdir}/*.a\n%endif\n%if @ENABLE_SHARED@\n%{_libdir}/*.so\n%endif\n%{_libdir}/pkgconfig/*.pc\n%{_includedir}/*\n%{_libdir}/cmake/ortp/*\n\n\n%changelog\n\n* Tue Nov 27 2018 ronan.abhamon <ronan.abhamon@belledonne-communications.com>\n- Do not set CMAKE_INSTALL_LIBDIR.\n\n* Tue Oct 25 2005 Francois-Xavier Kowalski <fix@hp.com>\n- Add to oRTP distribution with \"make rpm\" target\n"
  },
  {
    "path": "build/wp8/oRTP/inttypes.h",
    "content": "// ISO C9x  compliant inttypes.h for Microsoft Visual Studio\n// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124\n//\n//  Copyright (c) 2006 Alexander Chemeris\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are met:\n//\n//   1. Redistributions of source code must retain the above copyright notice,\n//      this list of conditions and the following disclaimer.\n//\n//   2. Redistributions in binary form must reproduce the above copyright\n//      notice, this list of conditions and the following disclaimer in the\n//      documentation and/or other materials provided with the distribution.\n//\n//   3. The name of the author may be used to endorse or promote products\n//      derived from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\n// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n//\n///////////////////////////////////////////////////////////////////////////////\n\n#ifndef _MSC_VER // [\n#error \"Use this header only with Microsoft Visual C++ compilers!\"\n#endif // _MSC_VER ]\n\n#ifndef _MSC_INTTYPES_H_ // [\n#define _MSC_INTTYPES_H_\n\n#if _MSC_VER > 1000\n#pragma once\n#endif\n\n#include \"stdint.h\"\n\n// 7.8 Format conversion of integer types\n\ntypedef struct {\n\tintmax_t quot;\n\tintmax_t rem;\n} imaxdiv_t;\n\n// 7.8.1 Macros for format specifiers\n\n#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [   See footnote 185 at page 198\n\n// The fprintf macros for signed integers are:\n#define PRId8 \"d\"\n#define PRIi8 \"i\"\n#define PRIdLEAST8 \"d\"\n#define PRIiLEAST8 \"i\"\n#define PRIdFAST8 \"d\"\n#define PRIiFAST8 \"i\"\n\n#define PRId16 \"hd\"\n#define PRIi16 \"hi\"\n#define PRIdLEAST16 \"hd\"\n#define PRIiLEAST16 \"hi\"\n#define PRIdFAST16 \"hd\"\n#define PRIiFAST16 \"hi\"\n\n#define PRId32 \"I32d\"\n#define PRIi32 \"I32i\"\n#define PRIdLEAST32 \"I32d\"\n#define PRIiLEAST32 \"I32i\"\n#define PRIdFAST32 \"I32d\"\n#define PRIiFAST32 \"I32i\"\n\n#define PRId64 \"I64d\"\n#define PRIi64 \"I64i\"\n#define PRIdLEAST64 \"I64d\"\n#define PRIiLEAST64 \"I64i\"\n#define PRIdFAST64 \"I64d\"\n#define PRIiFAST64 \"I64i\"\n\n#define PRIdMAX \"I64d\"\n#define PRIiMAX \"I64i\"\n\n#define PRIdPTR \"Id\"\n#define PRIiPTR \"Ii\"\n\n// The fprintf macros for unsigned integers are:\n#define PRIo8 \"o\"\n#define PRIu8 \"u\"\n#define PRIx8 \"x\"\n#define PRIX8 \"X\"\n#define PRIoLEAST8 \"o\"\n#define PRIuLEAST8 \"u\"\n#define PRIxLEAST8 \"x\"\n#define PRIXLEAST8 \"X\"\n#define PRIoFAST8 \"o\"\n#define PRIuFAST8 \"u\"\n#define PRIxFAST8 \"x\"\n#define PRIXFAST8 \"X\"\n\n#define PRIo16 \"ho\"\n#define PRIu16 \"hu\"\n#define PRIx16 \"hx\"\n#define PRIX16 \"hX\"\n#define PRIoLEAST16 \"ho\"\n#define PRIuLEAST16 \"hu\"\n#define PRIxLEAST16 \"hx\"\n#define PRIXLEAST16 \"hX\"\n#define PRIoFAST16 \"ho\"\n#define PRIuFAST16 \"hu\"\n#define PRIxFAST16 \"hx\"\n#define PRIXFAST16 \"hX\"\n\n#define PRIo32 \"I32o\"\n#define PRIu32 \"I32u\"\n#define PRIx32 \"I32x\"\n#define PRIX32 \"I32X\"\n#define PRIoLEAST32 \"I32o\"\n#define PRIuLEAST32 \"I32u\"\n#define PRIxLEAST32 \"I32x\"\n#define PRIXLEAST32 \"I32X\"\n#define PRIoFAST32 \"I32o\"\n#define PRIuFAST32 \"I32u\"\n#define PRIxFAST32 \"I32x\"\n#define PRIXFAST32 \"I32X\"\n\n#define PRIo64 \"I64o\"\n#define PRIu64 \"I64u\"\n#define PRIx64 \"I64x\"\n#define PRIX64 \"I64X\"\n#define PRIoLEAST64 \"I64o\"\n#define PRIuLEAST64 \"I64u\"\n#define PRIxLEAST64 \"I64x\"\n#define PRIXLEAST64 \"I64X\"\n#define PRIoFAST64 \"I64o\"\n#define PRIuFAST64 \"I64u\"\n#define PRIxFAST64 \"I64x\"\n#define PRIXFAST64 \"I64X\"\n\n#define PRIoMAX \"I64o\"\n#define PRIuMAX \"I64u\"\n#define PRIxMAX \"I64x\"\n#define PRIXMAX \"I64X\"\n\n#define PRIoPTR \"Io\"\n#define PRIuPTR \"Iu\"\n#define PRIxPTR \"Ix\"\n#define PRIXPTR \"IX\"\n\n// The fscanf macros for signed integers are:\n#define SCNd8 \"d\"\n#define SCNi8 \"i\"\n#define SCNdLEAST8 \"d\"\n#define SCNiLEAST8 \"i\"\n#define SCNdFAST8 \"d\"\n#define SCNiFAST8 \"i\"\n\n#define SCNd16 \"hd\"\n#define SCNi16 \"hi\"\n#define SCNdLEAST16 \"hd\"\n#define SCNiLEAST16 \"hi\"\n#define SCNdFAST16 \"hd\"\n#define SCNiFAST16 \"hi\"\n\n#define SCNd32 \"ld\"\n#define SCNi32 \"li\"\n#define SCNdLEAST32 \"ld\"\n#define SCNiLEAST32 \"li\"\n#define SCNdFAST32 \"ld\"\n#define SCNiFAST32 \"li\"\n\n#define SCNd64 \"I64d\"\n#define SCNi64 \"I64i\"\n#define SCNdLEAST64 \"I64d\"\n#define SCNiLEAST64 \"I64i\"\n#define SCNdFAST64 \"I64d\"\n#define SCNiFAST64 \"I64i\"\n\n#define SCNdMAX \"I64d\"\n#define SCNiMAX \"I64i\"\n\n#ifdef _WIN64 // [\n#define SCNdPTR \"I64d\"\n#define SCNiPTR \"I64i\"\n#else // _WIN64 ][\n#define SCNdPTR \"ld\"\n#define SCNiPTR \"li\"\n#endif // _WIN64 ]\n\n// The fscanf macros for unsigned integers are:\n#define SCNo8 \"o\"\n#define SCNu8 \"u\"\n#define SCNx8 \"x\"\n#define SCNX8 \"X\"\n#define SCNoLEAST8 \"o\"\n#define SCNuLEAST8 \"u\"\n#define SCNxLEAST8 \"x\"\n#define SCNXLEAST8 \"X\"\n#define SCNoFAST8 \"o\"\n#define SCNuFAST8 \"u\"\n#define SCNxFAST8 \"x\"\n#define SCNXFAST8 \"X\"\n\n#define SCNo16 \"ho\"\n#define SCNu16 \"hu\"\n#define SCNx16 \"hx\"\n#define SCNX16 \"hX\"\n#define SCNoLEAST16 \"ho\"\n#define SCNuLEAST16 \"hu\"\n#define SCNxLEAST16 \"hx\"\n#define SCNXLEAST16 \"hX\"\n#define SCNoFAST16 \"ho\"\n#define SCNuFAST16 \"hu\"\n#define SCNxFAST16 \"hx\"\n#define SCNXFAST16 \"hX\"\n\n#define SCNo32 \"lo\"\n#define SCNu32 \"lu\"\n#define SCNx32 \"lx\"\n#define SCNX32 \"lX\"\n#define SCNoLEAST32 \"lo\"\n#define SCNuLEAST32 \"lu\"\n#define SCNxLEAST32 \"lx\"\n#define SCNXLEAST32 \"lX\"\n#define SCNoFAST32 \"lo\"\n#define SCNuFAST32 \"lu\"\n#define SCNxFAST32 \"lx\"\n#define SCNXFAST32 \"lX\"\n\n#define SCNo64 \"I64o\"\n#define SCNu64 \"I64u\"\n#define SCNx64 \"I64x\"\n#define SCNX64 \"I64X\"\n#define SCNoLEAST64 \"I64o\"\n#define SCNuLEAST64 \"I64u\"\n#define SCNxLEAST64 \"I64x\"\n#define SCNXLEAST64 \"I64X\"\n#define SCNoFAST64 \"I64o\"\n#define SCNuFAST64 \"I64u\"\n#define SCNxFAST64 \"I64x\"\n#define SCNXFAST64 \"I64X\"\n\n#define SCNoMAX \"I64o\"\n#define SCNuMAX \"I64u\"\n#define SCNxMAX \"I64x\"\n#define SCNXMAX \"I64X\"\n\n#ifdef _WIN64 // [\n#define SCNoPTR \"I64o\"\n#define SCNuPTR \"I64u\"\n#define SCNxPTR \"I64x\"\n#define SCNXPTR \"I64X\"\n#else // _WIN64 ][\n#define SCNoPTR \"lo\"\n#define SCNuPTR \"lu\"\n#define SCNxPTR \"lx\"\n#define SCNXPTR \"lX\"\n#endif // _WIN64 ]\n\n#endif // __STDC_FORMAT_MACROS ]\n\n// 7.8.2 Functions for greatest-width integer types\n\n// 7.8.2.1 The imaxabs function\n#define imaxabs _abs64\n\n// 7.8.2.2 The imaxdiv function\n\n// This is modified version of div() function from Microsoft's div.c found\n// in %MSVC.NET%\\crt\\src\\div.c\n#ifdef STATIC_IMAXDIV // [\nstatic\n#else  // STATIC_IMAXDIV ][\n_inline\n#endif // STATIC_IMAXDIV ]\n    imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) {\n\timaxdiv_t result;\n\n\tresult.quot = numer / denom;\n\tresult.rem = numer % denom;\n\n\tif (numer < 0 && result.rem > 0) {\n\t\t// did division wrong; must fix up\n\t\t++result.quot;\n\t\tresult.rem -= denom;\n\t}\n\n\treturn result;\n}\n\n// 7.8.2.3 The strtoimax and strtoumax functions\n#define strtoimax _strtoi64\n#define strtoumax _strtoui64\n\n// 7.8.2.4 The wcstoimax and wcstoumax functions\n#define wcstoimax _wcstoi64\n#define wcstoumax _wcstoui64\n\n#endif // _MSC_INTTYPES_H_ ]\n"
  },
  {
    "path": "build/wp8/oRTP/oRTP.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Express 2012 for Windows Phone\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"oRTP\", \"oRTP.vcxproj\", \"{FFC7B532-0502-4D88-AC98-9E89071CBC97}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"srtp\", \"..\\..\\..\\..\\..\\srtp\\build\\wp8\\srtp\\srtp.vcxproj\", \"{B4B96BC4-2B72-4964-98E4-7FD048A43363}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|ARM = Debug|ARM\n\t\tDebug|Win32 = Debug|Win32\n\t\tRelease|ARM = Release|ARM\n\t\tRelease|Win32 = Release|Win32\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|ARM.ActiveCfg = Debug|ARM\n\t\t{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|ARM.Build.0 = Debug|ARM\n\t\t{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|Win32.ActiveCfg = Debug|Win32\n\t\t{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Debug|Win32.Build.0 = Debug|Win32\n\t\t{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|ARM.ActiveCfg = Release|ARM\n\t\t{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|ARM.Build.0 = Release|ARM\n\t\t{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|Win32.ActiveCfg = Release|Win32\n\t\t{FFC7B532-0502-4D88-AC98-9E89071CBC97}.Release|Win32.Build.0 = Release|Win32\n\t\t{B4B96BC4-2B72-4964-98E4-7FD048A43363}.Debug|ARM.ActiveCfg = Debug|ARM\n\t\t{B4B96BC4-2B72-4964-98E4-7FD048A43363}.Debug|ARM.Build.0 = Debug|ARM\n\t\t{B4B96BC4-2B72-4964-98E4-7FD048A43363}.Debug|Win32.ActiveCfg = Debug|Win32\n\t\t{B4B96BC4-2B72-4964-98E4-7FD048A43363}.Debug|Win32.Build.0 = Debug|Win32\n\t\t{B4B96BC4-2B72-4964-98E4-7FD048A43363}.Release|ARM.ActiveCfg = Release|ARM\n\t\t{B4B96BC4-2B72-4964-98E4-7FD048A43363}.Release|ARM.Build.0 = Release|ARM\n\t\t{B4B96BC4-2B72-4964-98E4-7FD048A43363}.Release|Win32.ActiveCfg = Release|Win32\n\t\t{B4B96BC4-2B72-4964-98E4-7FD048A43363}.Release|Win32.Build.0 = Release|Win32\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "build/wp8/oRTP/oRTP.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|ARM\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>ARM</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|ARM\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>ARM</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{ffc7b532-0502-4d88-ac98-9e89071cbc97}</ProjectGuid>\r\n    <RootNamespace>oRTP</RootNamespace>\r\n    <DefaultLanguage>en-US</DefaultLanguage>\r\n    <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)'=='Debug'\" Label=\"Configuration\">\r\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v110_wp80</PlatformToolset>\r\n    <IgnoreImportLibrary>false</IgnoreImportLibrary>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)'=='Release'\" Label=\"Configuration\">\r\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <PlatformToolset>v110_wp80</PlatformToolset>\r\n    <IgnoreImportLibrary>false</IgnoreImportLibrary>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup>\r\n    <OutDir>$(SolutionDir)$(Platform)\\$(Configuration)\\</OutDir>\r\n    <IntDir>$(SolutionDir)$(Platform)\\$(Configuration)\\$(TargetName)\\</IntDir>\r\n  </PropertyGroup>\r\n  <PropertyGroup>\r\n    <GenerateManifest>false</GenerateManifest>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup>\r\n    <ClCompile>\r\n      <WarningLevel>Level4</WarningLevel>\r\n      <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)..\\..\\..\\include;$(ProjectDir)..\\..\\..\\..\\..\\srtp\\include;$(ProjectDir)..\\..\\..\\..\\..\\srtp\\crypto\\include;$(ProjectDir)..\\..\\..\\..\\..\\srtp\\build\\wp8\\srtp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>_WIN32;_WINDLL;_USRDLL;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;ORTP_INET6;WIN32;ORTP_EXPORTS;WINDOWS_NATIVE;HAVE_SRTP;ORTP_VERSION=\"0.23.0\";ORTP_MAJOR_VERSION=0;ORTP_MINOR_VERSION=23;ORTP_MICRO_VERSION=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <BasicRuntimeChecks>Default</BasicRuntimeChecks>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <CompileAsWinRT>false</CompileAsWinRT>\r\n      <AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>\r\n      <GenerateWindowsMetadata>false</GenerateWindowsMetadata>\r\n      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n      <ImportLibrary>$(TargetDir)$(TargetName).lib</ImportLibrary>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)'=='Debug'\">\r\n    <ClCompile>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ClCompile>\r\n    <Link>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)'=='Release'\">\r\n    <ClCompile>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <StringPooling>true</StringPooling>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n    </ClCompile>\r\n    <Link>\r\n      <GenerateDebugInformation>false</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\b64.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\event.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\logging.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\ortp.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\payloadtype.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\port.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\rtcp.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\rtp.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\rtpprofile.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\rtpsession.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\rtpsignaltable.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\sessionset.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\str_utils.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\include\\ortp\\telephonyevents.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\src\\jitterctl.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\src\\rtpsession_priv.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\src\\rtptimer.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\src\\scheduler.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\src\\utils.h\" />\r\n    <ClInclude Include=\"..\\..\\..\\src\\winrttimer.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"..\\..\\..\\src\\avprofile.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\b64.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\dll_entry.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\event.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\extremum.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\jitterctl.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\logging.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\netsim.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\ortp.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\payloadtype.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\port.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\posixtimer.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\rtcp.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\rtcp_fb.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\rtcp_xr.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\rtcpparse.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\rtpparse.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\rtpprofile.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\rtpsession.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\rtpsession_inet.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\rtpsignaltable.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\rtptimer.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\scheduler.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\sessionset.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\str_utils.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\telephonyevents.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\utils.c\" />\r\n    <ClCompile Include=\"..\\..\\..\\src\\winrttimer.cpp\">\r\n      <CompileAsWinRT>true</CompileAsWinRT>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Reference Include=\"Windows\">\r\n      <IsWinMDFile>true</IsWinMDFile>\r\n    </Reference>\r\n    <Reference Include=\"platform.winmd\">\r\n      <IsWinMDFile>true</IsWinMDFile>\r\n      <Private>false</Private>\r\n    </Reference>\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <Import Project=\"$(MSBuildExtensionsPath)\\Microsoft\\WindowsPhone\\v$(TargetPlatformVersion)\\Microsoft.Cpp.WindowsPhone.$(TargetPlatformVersion).targets\" />\r\n</Project>"
  },
  {
    "path": "build/wp8/oRTP/stdint.h",
    "content": "// ISO C9x  compliant stdint.h for Microsoft Visual Studio\n// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124\n//\n//  Copyright (c) 2006-2008 Alexander Chemeris\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are met:\n//\n//   1. Redistributions of source code must retain the above copyright notice,\n//      this list of conditions and the following disclaimer.\n//\n//   2. Redistributions in binary form must reproduce the above copyright\n//      notice, this list of conditions and the following disclaimer in the\n//      documentation and/or other materials provided with the distribution.\n//\n//   3. The name of the author may be used to endorse or promote products\n//      derived from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\n// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n//\n///////////////////////////////////////////////////////////////////////////////\n\n#ifndef _MSC_VER // [\n#error \"Use this header only with Microsoft Visual C++ compilers!\"\n#endif // _MSC_VER ]\n\n#ifndef _MSC_STDINT_H_ // [\n#define _MSC_STDINT_H_\n\n#if _MSC_VER > 1000\n#pragma once\n#endif\n\n#include <limits.h>\n\n// For Visual Studio 6 in C++ mode and for many Visual Studio versions when\n// compiling for ARM we should wrap <wchar.h> include with 'extern \"C++\" {}'\n// or compiler give many errors like this:\n//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#include <wchar.h>\n#ifdef __cplusplus\n}\n#endif\n\n// Define _W64 macros to mark types changing their size, like intptr_t.\n#ifndef _W64\n#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300\n#define _W64 __w64\n#else\n#define _W64\n#endif\n#endif\n\n// 7.18.1 Integer types\n\n// 7.18.1.1 Exact-width integer types\n\n// Visual Studio 6 and Embedded Visual C++ 4 doesn't\n// realize that, e.g. char has the same size as __int8\n// so we give up on __intX for them.\n#if (_MSC_VER < 1300)\ntypedef signed char int8_t;\ntypedef signed short int16_t;\ntypedef signed int int32_t;\ntypedef unsigned char uint8_t;\ntypedef unsigned short uint16_t;\ntypedef unsigned int uint32_t;\n#else\ntypedef signed __int8 int8_t;\ntypedef signed __int16 int16_t;\ntypedef signed __int32 int32_t;\ntypedef unsigned __int8 uint8_t;\ntypedef unsigned __int16 uint16_t;\ntypedef unsigned __int32 uint32_t;\n#endif\ntypedef signed __int64 int64_t;\ntypedef unsigned __int64 uint64_t;\n\n// 7.18.1.2 Minimum-width integer types\ntypedef int8_t int_least8_t;\ntypedef int16_t int_least16_t;\ntypedef int32_t int_least32_t;\ntypedef int64_t int_least64_t;\ntypedef uint8_t uint_least8_t;\ntypedef uint16_t uint_least16_t;\ntypedef uint32_t uint_least32_t;\ntypedef uint64_t uint_least64_t;\n\n// 7.18.1.3 Fastest minimum-width integer types\ntypedef int8_t int_fast8_t;\ntypedef int16_t int_fast16_t;\ntypedef int32_t int_fast32_t;\ntypedef int64_t int_fast64_t;\ntypedef uint8_t uint_fast8_t;\ntypedef uint16_t uint_fast16_t;\ntypedef uint32_t uint_fast32_t;\ntypedef uint64_t uint_fast64_t;\n\n// 7.18.1.4 Integer types capable of holding object pointers\n#ifdef _WIN64 // [\ntypedef signed __int64 intptr_t;\ntypedef unsigned __int64 uintptr_t;\n#else  // _WIN64 ][\ntypedef _W64 signed int intptr_t;\ntypedef _W64 unsigned int uintptr_t;\n#endif // _WIN64 ]\n\n// 7.18.1.5 Greatest-width integer types\ntypedef int64_t intmax_t;\ntypedef uint64_t uintmax_t;\n\n// 7.18.2 Limits of specified-width integer types\n\n#if !defined(__cplusplus) ||                                                                                           \\\n    defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259\n\n// 7.18.2.1 Limits of exact-width integer types\n#define INT8_MIN ((int8_t)_I8_MIN)\n#define INT8_MAX _I8_MAX\n#define INT16_MIN ((int16_t)_I16_MIN)\n#define INT16_MAX _I16_MAX\n#define INT32_MIN ((int32_t)_I32_MIN)\n#define INT32_MAX _I32_MAX\n#define INT64_MIN ((int64_t)_I64_MIN)\n#define INT64_MAX _I64_MAX\n#define UINT8_MAX _UI8_MAX\n#define UINT16_MAX _UI16_MAX\n#define UINT32_MAX _UI32_MAX\n#define UINT64_MAX _UI64_MAX\n\n// 7.18.2.2 Limits of minimum-width integer types\n#define INT_LEAST8_MIN INT8_MIN\n#define INT_LEAST8_MAX INT8_MAX\n#define INT_LEAST16_MIN INT16_MIN\n#define INT_LEAST16_MAX INT16_MAX\n#define INT_LEAST32_MIN INT32_MIN\n#define INT_LEAST32_MAX INT32_MAX\n#define INT_LEAST64_MIN INT64_MIN\n#define INT_LEAST64_MAX INT64_MAX\n#define UINT_LEAST8_MAX UINT8_MAX\n#define UINT_LEAST16_MAX UINT16_MAX\n#define UINT_LEAST32_MAX UINT32_MAX\n#define UINT_LEAST64_MAX UINT64_MAX\n\n// 7.18.2.3 Limits of fastest minimum-width integer types\n#define INT_FAST8_MIN INT8_MIN\n#define INT_FAST8_MAX INT8_MAX\n#define INT_FAST16_MIN INT16_MIN\n#define INT_FAST16_MAX INT16_MAX\n#define INT_FAST32_MIN INT32_MIN\n#define INT_FAST32_MAX INT32_MAX\n#define INT_FAST64_MIN INT64_MIN\n#define INT_FAST64_MAX INT64_MAX\n#define UINT_FAST8_MAX UINT8_MAX\n#define UINT_FAST16_MAX UINT16_MAX\n#define UINT_FAST32_MAX UINT32_MAX\n#define UINT_FAST64_MAX UINT64_MAX\n\n// 7.18.2.4 Limits of integer types capable of holding object pointers\n#ifdef _WIN64 // [\n#define INTPTR_MIN INT64_MIN\n#define INTPTR_MAX INT64_MAX\n#define UINTPTR_MAX UINT64_MAX\n#else // _WIN64 ][\n#define INTPTR_MIN INT32_MIN\n#define INTPTR_MAX INT32_MAX\n#define UINTPTR_MAX UINT32_MAX\n#endif // _WIN64 ]\n\n// 7.18.2.5 Limits of greatest-width integer types\n#define INTMAX_MIN INT64_MIN\n#define INTMAX_MAX INT64_MAX\n#define UINTMAX_MAX UINT64_MAX\n\n// 7.18.3 Limits of other integer types\n\n#ifdef _WIN64 // [\n#define PTRDIFF_MIN _I64_MIN\n#define PTRDIFF_MAX _I64_MAX\n#else // _WIN64 ][\n#define PTRDIFF_MIN _I32_MIN\n#define PTRDIFF_MAX _I32_MAX\n#endif // _WIN64 ]\n\n#define SIG_ATOMIC_MIN INT_MIN\n#define SIG_ATOMIC_MAX INT_MAX\n\n#ifndef SIZE_MAX // [\n#ifdef _WIN64    // [\n#define SIZE_MAX _UI64_MAX\n#else // _WIN64 ][\n#define SIZE_MAX _UI32_MAX\n#endif // _WIN64 ]\n#endif // SIZE_MAX ]\n\n// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>\n#ifndef WCHAR_MIN // [\n#define WCHAR_MIN 0\n#endif            // WCHAR_MIN ]\n#ifndef WCHAR_MAX // [\n#define WCHAR_MAX _UI16_MAX\n#endif // WCHAR_MAX ]\n\n#define WINT_MIN 0\n#define WINT_MAX _UI16_MAX\n\n#endif // __STDC_LIMIT_MACROS ]\n\n// 7.18.4 Limits of other integer types\n\n#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260\n\n// 7.18.4.1 Macros for minimum-width integer constants\n\n#define INT8_C(val) val##i8\n#define INT16_C(val) val##i16\n#define INT32_C(val) val##i32\n#define INT64_C(val) val##i64\n\n#define UINT8_C(val) val##ui8\n#define UINT16_C(val) val##ui16\n#define UINT32_C(val) val##ui32\n#define UINT64_C(val) val##ui64\n\n// 7.18.4.2 Macros for greatest-width integer constants\n#define INTMAX_C INT64_C\n#define UINTMAX_C UINT64_C\n\n#endif // __STDC_CONSTANT_MACROS ]\n\n#endif // _MSC_STDINT_H_ ]\n"
  },
  {
    "path": "cmake/OrtpConfig.cmake.in",
    "content": "############################################################################\n# OrtpConfig.cmake.in\n# Copyright (C) 2015-2023  Belledonne Communications, Grenoble France\n#\n############################################################################\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n#\n############################################################################\n#\n# Config file for the ortp package.\n#\n# Targets\n# ^^^^^^^\n#\n# The following targets are defined:\n#  ortp - The ortp library target\n#\n#\n# Result variables\n# ^^^^^^^^^^^^^^^^\n#\n# This config file will set the following variables in your project:\n#\n#  Ortp_FOUND - The ortp library has been found\n#  Ortp_TARGET - The name of the CMake target for the ortp library\n\n\n@PACKAGE_INIT@\n\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/OrtpTargets.cmake\")\n\nset(Ortp_TARGET ortp)\n\n# We must propagate the public dependencies and the private dependencies for static build\ninclude(CMakeFindDependencyMacro)\nfind_dependency(BCToolbox)\n\ncheck_required_components(Ortp)\n"
  },
  {
    "path": "configure.ac",
    "content": "dnl Process this file with autoconf to produce a configure script.\nAC_INIT([ortp],[1.0.1])\nAC_CANONICAL_SYSTEM\n\ncase $INSTALL in\n        *ginstall*)\n                INSTALL=\"$INSTALL -C\"\n        ;;\nesac\n\ndnl Source packaging numbers\nORTP_MAJOR_VERSION=$(echo $PACKAGE_VERSION | cut -d. -f1)\nORTP_MINOR_VERSION=$(echo $PACKAGE_VERSION | cut -d. -f2)\nORTP_MICRO_VERSION=$(echo $PACKAGE_VERSION | cut -d. -f3)\nORTP_EXTRA_VERSION=$(echo $PACKAGE_VERSION | cut -d. -f4)\n\nLIBORTP_SO_CURRENT=13 dnl increment this number when you add/change/remove an interface\nLIBORTP_SO_REVISION=0 dnl increment this number when you change source code, without changing interfaces; set to 0 when incrementing CURRENT\nLIBORTP_SO_AGE=0 dnl increment this number when you add an interface, set to 0 if you remove an interface\n\nLIBORTP_SO_VERSION=$LIBORTP_SO_CURRENT:$LIBORTP_SO_REVISION:$LIBORTP_SO_AGE\nORTP_VERSION=${ORTP_MAJOR_VERSION}.${ORTP_MINOR_VERSION}.${ORTP_MICRO_VERSION}\n\nif test -n \"$ORTP_EXTRA_VERSION\" ; then\n\tORTP_VERSION=\"${ORTP_VERSION}.${ORTP_EXTRA_VERSION}\"\nfi\n\nORTP_PKGCONFIG_VERSION=${ORTP_VERSION}\n\nAC_SUBST(LIBORTP_SO_CURRENT, $LIBORTP_SO_CURRENT)\nAC_SUBST(LIBORTP_SO_VERSION)\nAC_SUBST(ORTP_VERSION)\nAC_SUBST(ORTP_PKGCONFIG_VERSION)\n\n\nPACKAGE=ortp\n\nAM_INIT_AUTOMAKE([tar-ustar foreign])\nm4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],)\nAC_SUBST([docdir], [${datadir}/doc])\nAC_CONFIG_HEADERS(ortp-config.h)\nAC_CONFIG_MACRO_DIR([m4])\nAC_DEFINE_UNQUOTED(ORTP_MAJOR_VERSION,$ORTP_MAJOR_VERSION, [major version])\nAC_DEFINE_UNQUOTED(ORTP_MINOR_VERSION,$ORTP_MINOR_VERSION, [minor version])\nAC_DEFINE_UNQUOTED(ORTP_MICRO_VERSION,$ORTP_MICRO_VERSION, [micro version])\nAC_DEFINE_UNQUOTED(ORTP_VERSION,\"$ORTP_VERSION\",[ortp version number])\n\ndnl Checks for programs.\nAC_PROG_CC\nLT_INIT([win32-dll shared disable-static])\n\ngl_LD_OUTPUT_DEF\n\nAC_MSG_CHECKING([warning make an error on compilation])\n\nAC_ARG_ENABLE(strict,\n\tAC_HELP_STRING([--enable-strict], [Build with stricter options @<:@yes@:>@]),\n\t[strictness=\"${enableval}\"],\n\t[strictness=yes]\n)\n\nSTRICT_OPTIONS=\"-Wall -Wuninitialized\"\nSTRICT_OPTIONS_CC=\"-Wdeclaration-after-statement -Wstrict-prototypes\"\nSTRICT_OPTIONS_CXX=\"\"\n\n#for clang\n\ncase $CC in\n\t*clang*)\n\t\tSTRICT_OPTIONS=\"$STRICT_OPTIONS -Qunused-arguments \"\n\t;;\nesac\n\n# because Darwin's gcc is actually clang, we need to check it...\ncase \"$target_os\" in\n\t*darwin*)\n\tSTRICT_OPTIONS=\"$STRICT_OPTIONS -Wno-error=unknown-warning-option -Qunused-arguments -Wno-tautological-compare -Wno-unused-function \"\n\t#disabled due to wrong optimization false positive with small string\n\t#(cf. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35903)\n\tSTRICT_OPTIONS=\"$STRICT_OPTIONS -Wno-array-bounds \"\n\t;;\nesac\n\nif test \"$strictness\" = \"yes\" ; then\n\tSTRICT_OPTIONS=\"$STRICT_OPTIONS -Werror -Wextra -Wunused-parameter -Wno-missing-field-initializers\"\n\tCFLAGS=\"$CFLAGS -fno-strict-aliasing\"\nfi\n\nAC_SUBST(STRICT_OPTIONS)\nAC_SUBST(STRICT_OPTIONS_CC)\nAC_SUBST(STRICT_OPTIONS_CXX)\n\nAC_ARG_ENABLE(perf,\n\t[AS_HELP_STRING([--enable-perf], [Disable costly features to reduce cpu consumtion (default=no)])],\n\t[perf=$enableval],\n\t[perf=no]\n)\n\nORTP_DEFS=\n\nAC_DEFINE(__APPLE_USE_RFC_3542, 1, [ Apple wants you to declare what behavior you want by defining either __APPLE_USE_RFC_3542])\n\n\ndnl enable timestamp support\nAC_ARG_ENABLE(ntp-timestamp,\n\t[AS_HELP_STRING([--enable-ntp-timestamp], [Turn on NTP timestamping on received packet (default=no)])],\n\t[case \"${enableval}\" in\n\t\tyes)\tntptimestamp=true;;\n\t\tno)\tntptimestamp=false;;\n\t\t*)\tAC_MSG_ERROR(bad value ${enableval} for --enable-ntp-timestamp) ;;\n\tesac],\n\t[ntptimestamp=false]\n)\nif test x$ntptimestamp = xtrue ; then\n\tORTP_DEFS=\"$ORTP_DEFS -DORTP_TIMESTAMP\"\nfi\n\nAC_ARG_ENABLE(mode64bit,\n\t[AS_HELP_STRING([--enable-mode64bit], [Produce a 64-bit library (default=no)])],\n\t[case \"${enableval}\" in\n\t\tyes)\tmode64bit_enabled=yes;;\n\t\tno)\tmode64bit_enabled=no;;\n\t\t*)\tAC_MSG_ERROR(\"Bad value for --enable-mode64bit\");;\n\tesac],\n\t[mode64bit_enabled=no]\n)\n\nAC_ARG_ENABLE(debug,\n\t[AS_HELP_STRING([--enable-debug], [Enable the display of traces showing the execution of the library (default=yes)])],\n\t[case \"${enableval}\" in\n\t\tyes)\tdebug_enabled=yes;;\n\t\tno)\tdebug_enabled=no;;\n\t\t*)\tAC_MSG_ERROR(\"Bad value for --enable-debug\");;\n\tesac],\n\t[debug_enabled=no]\n)\n\nhpux_host=no\nposixtimer_interval=10000\nPTHREAD_LDFLAGS=\n\ncase \"$target_os\" in\n\t*hpux*)\n\t\thpux_host=yes\n\t\tAC_DEFINE(NOCONNECT,1,[Defined if we should not use connect() on udp sockets])\n\t\tCFLAGS=\"$CFLAGS -D_HPUX_SOURCE -D_XOPEN_SOURCE_EXTENDED -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=199506L\"\n\t\tLIBS=\"$LIBS -lxnet\"\n\t;;\n\t*freebsd*)\n\t\tAC_DEFINE(NOCONNECT,1,[Defined if we should not use connect() on udp sockets])\n\t\tPTHREAD_LDFLAGS=\"-pthread\"\n\t;;\n\t*mingw32ce)\n\t\tCFLAGS=\"$CFLAGS -D_WIN32_WCE -D_WIN32_WINNT=0x0501 -DORTP_STATIC\"\n\t\tLIBS=\"$LIBS -lws2 -liphlpapi\"\n\t\tmingw_found=yes\n\t;;\n\t*mingw*)\n\t\tCFLAGS=\"$CFLAGS -D_WIN32_WINNT=0x0501 -DORTP_STATIC\"\n\t\tLIBS=\"$LIBS -lws2_32 -liphlpapi -lwinmm\"\n\t\tmingw_found=yes\n\t;;\nesac\n\nAM_CONDITIONAL(BUILD_WIN32, test \"$mingw_found\" = \"yes\")\nAC_CONFIG_COMMANDS([libtool-hacking],\n\t[if test \"$mingw_found\" = \"yes\" ; then\n\t\techo \"Hacking libtool to work with mingw...\"\n\t\tsed -e 's/\\*\\\" \\$a_deplib \\\"\\*/\\*/' < ./libtool > libtool.tmp\n\t\tcp -f ./libtool.tmp ./libtool\n\t\trm -f ./libtool.tmp\n\tfi],\n\t[mingw_found=$mingw_found]\n)\n\nif test \"$GCC\" != \"yes\" ; then\n\tif test \"$hpux_host\" = \"yes\" ; then\n\t\tdnl we are probably using HPUX cc compiler, so add a +O2 to CFLAGS\n\t\t\tCFLAGS=\"$CFLAGS +O2 -g \"\n\t\tif test x$mode64bit_enabled = xyes ; then\n\t\t\tCFLAGS=\"$CFLAGS +DA2.0W +DS2.0\"\n\t\tfi\n\tfi\nelse\n\tCFLAGS=\"$CFLAGS -Wall\"\nfi\n\nbuild_scheduler=yes\n\ndnl Check if we have seteuid system call\nAC_CHECK_FUNCS(seteuid)\n\ndnl Check if we have arc4random family routines available\nAC_CHECK_FUNCS(arc4random)\n\n\ndnl check if we can use the pthread_library\nAC_CHECK_LIB(pthread, pthread_mutex_init, [pthread_enabled=yes], [pthread_enabled=no])\nif test $pthread_enabled = \"no\" ; then\n\tbuild_scheduler=no\nelse\n\tPTHREAD_LIBS=\"-lpthread\"\n\tPTHREAD_CFLAGS=\"-D_REENTRANT\"\n\tAC_SUBST(PTHREAD_CFLAGS)\n\tAC_SUBST(PTHREAD_LIBS)\n\tAC_SUBST(PTHREAD_LDFLAGS)\nfi\nAC_ARG_WITH(thread-stack-size,\n\tAC_HELP_STRING([--with-thread-stack-size=SIZE-IN-BYTES],[Set thread stack size [[default=os-default]]]),\n\t[thread_stack_size=$withval],\n\t[thread_stack_size=0]\n)\nAC_DEFINE_UNQUOTED(ORTP_DEFAULT_THREAD_STACK_SIZE, $thread_stack_size, [Default thread stack size (0 = let uperating system decide)])\n\n\ndnl check if we can use the rt library\nAC_CHECK_LIB(rt, clock_gettime, [rt_enabled=yes])\nif test \"$rt_enabled\" = \"yes\" ; then\n\tRT_LIBS=\"-lrt\"\n\tAC_SUBST(RT_LIBS)\nfi\n\n\nif test $debug_enabled = \"yes\"; then\n\tORTP_DEFS=\"$ORTP_DEFS -DORTP_DEBUG_MODE\"\n\tCFLAGS=`echo $CFLAGS | sed 's/-O.//'`\n\tCFLAGS=\"$CFLAGS -g\"\nfi\n\n\nAC_ARG_ENABLE(memcheck,\n\t[AS_HELP_STRING([--enable-memcheck], [Enable memory leak detection (HPUX only)])],\n\t[case \"${enableval}\" in\n\t\tyes)\tmemcheck_enabled=yes;;\n\t\tno)\tmemcheck_enabled=no;;\n\t\t*)\tAC_MSG_ERROR(\"Bad value for --enable-memcheck\");;\n\tesac],\n\t[memcheck_enabled=no]\n)\n\nif test \"$memcheck_enabled\" = \"yes\" ; then\n\tif test \"$hpux_host\" = \"yes\" ; then\n\t\tAC_DEFINE(ENABLE_MEMCHECK,1,[Defined when memory leak checking if enabled])\n\telse\n\t\techo \"WARNING ************ : the memory check option is only available for HPUX.\"\n\tfi\nfi\n\n\ndnl Checks for header files.\nAC_HEADER_STDC\nAC_CHECK_HEADERS(poll.h sys/poll.h sys/uio.h fcntl.h sys/time.h unistd.h sys/audio.h linux/soundcard.h sys/shm.h stdatomic.h)\n\ndnl Checks for typedefs, structures, and compiler characteristics.\nAC_C_CONST\nAC_C_INLINE\nAC_HEADER_TIME\nAC_WORDS_BIGENDIAN\nif test x$ac_cv_c_bigendian = xyes ; then\n\tORTP_DEFS=\"$ORTP_DEFS -DORTP_BIGENDIAN\"\nfi\n\ndnl Checks for library functions.\nAC_CHECK_FUNCS(select socket strerror)\n\nif test $hpux_host = \"yes\" ; then\ndnl it seems 10 ms is too fast on hpux and it causes trouble\n\t\tposixtimer_interval=20000\nfi\n\nAC_DEFINE_UNQUOTED(POSIXTIMER_INTERVAL,$posixtimer_interval,[Defines the periodicity of the rtp scheduler in microseconds])\n\nif test \"$perf\" = \"yes\" ; then\n\tCFLAGS=\"$CFLAGS -DPERF\"\nfi\n\n\nPKG_CHECK_MODULES(BCTOOLBOX, bctoolbox, [found_bctoolbox=yes],[found_bctoolbox=no])\nif test \"x$found_bctoolbox\" != \"xyes\" ; then\n\tAC_MSG_ERROR([\"Could not find bctoolbox (required dependency)\"])\nfi\n\n\nORTPDEPS_LIBS=\"$ORTPDEPS_LIBS $PTHREAD_LDFLAGS $BCTOOLBOX_LIBS\"\nORTPDEPS_CFLAGS=\"$ORTPDEPS_CFLAGS $PTHREAD_CFLAGS $ORTP_DEFS $BCTOOLBOX_CFLAGS\"\nCFLAGS=\"$CFLAGS $ORTP_DEFS\"\necho \"$ORTPDEPS_CFLAGS\" > ortp.defs\n\n\nAC_ARG_ENABLE(tests,\n\t[AS_HELP_STRING([--disable-tests], [Disable compilation of tests])],\n\t[case \"${enableval}\" in\n\t\tyes)\ttests_enabled=true ;;\n\t\tno)\ttests_enabled=false ;;\n\t\t*)\tAC_MSG_ERROR(bad value ${enableval} for --disable-tests) ;;\n\tesac],\n\t[tests_enabled=false]\n)\nAM_CONDITIONAL(ENABLE_TESTS, test x$tests_enabled = xtrue)\n\ncase \"$target_os\" in\n\t*linux*)\n\t\t# Eliminate -lstdc++ addition to postdeps for cross compiles.\n\t\tpostdeps_CXX=`echo \" $postdeps_CXX \" | sed 's, -lstdc++ ,,g'`\n\t;;\nesac\n\ndnl ##################################################\ndnl # Check for doxygen\ndnl ##################################################\nAC_ARG_ENABLE(documentation,\n\t[AS_HELP_STRING([--enable-documentation], [Documentation generation using doxygen (default=yes)])],\n\t[case \"${enableval}\" in\n\t\tyes)\tdocumentation_enabled=yes;;\n\t\tno)\t\tdocumentation_enabled=no;;\n\t\t*)\t\tAC_MSG_ERROR(\"Bad value for --enable-documentation\");;\n\tesac],\n\t[documentation_enabled=yes]\n)\nif test \"$documentation_enabled\" = \"yes\" ; then\n\tAC_CHECK_PROG(DOXYGEN,doxygen,doxygen,false)\nelse\n\tDOXYGEN=false\nfi\nAM_CONDITIONAL(HAVE_DOXYGEN, test \"$DOXYGEN\" != \"false\")\n\ndnl ##################################################\ndnl # Check for ESP Packager\ndnl ##################################################\n\nAC_PATH_PROG(EPM,epm,false)\nAC_PATH_PROG(MKEPMLIST,mkepmlist,false)\nAC_PATH_PROG(EPMINSTALL,epminstall,false)\nAM_CONDITIONAL(WITH_EPM,test $EPM != false && test $MKEPMLIST != false && test $EPMINSTALL != false)\n\n# Preferred packaging system, as per EPM terminology\ncase $target in\n\t*-*-linux*)\n\t\tif test -f /etc/debian_version ; then\n\t\t\tEPM_PKG_EXT=deb\n\t\telse\n\t\t\tEPM_PKG_EXT=rpm\n\t\tfi\n\t;;\n\t*-hp-hpux*)\n\t\tEPM_PKG_EXT=depot.gz\n\t;;\n\t*-dec-osf*)\n\t\tEPM_PKG_EXT=setld\n\t;;\nesac\nAC_SUBST(EPM_PKG_EXT)\n\n# System software User & Group names\ncase $target in\n\t*-*-linux*)\n\t\tSYS_USER=root\n\t\tSYS_GROUP=root\n\t;;\n\t*-*-hpux*|*-dec-osf*)\n\t\tSYS_USER=bin\n\t\tSYS_GROUP=bin\n\t;;\nesac\nAC_SUBST(SYS_USER)\nAC_SUBST(SYS_GROUP)\n\n# CPU Architecture\ncase $target_cpu in\n\ti?86)\n\t\tARCH=i386\n\t;;\n\t*)\tARCH=$target_cpu\n\t;;\nesac\nAC_SUBST(ARCH)\n\n# Various other packaging variables, that can be over-ridden ad `make\n# package' time\nSUMMARY=\"Implementation of RTP - RFC3550\"\nAC_SUBST(SUMMARY)\nPACKAGER=anonymous\nAC_SUBST(PACKAGER)\nLICENSE=GPLv2+\nAC_SUBST(LICENSE)\nVENDOR=Linphone\nAC_SUBST(VENDOR)\nRELEASE=1\nAC_SUBST(RELEASE)\n\nAC_SUBST(ORTPDEPS_CFLAGS)\nAC_SUBST(ORTPDEPS_LIBS)\nAC_SUBST(ORTPDEPS_LDFLAGS)\n\nAC_OUTPUT(\n\tMakefile\n\tinclude/Makefile\n\tinclude/ortp/Makefile\n\tm4/Makefile\n\tsrc/Makefile\n\tsrc/tests/Makefile\n\tsrc/tests/win_receiver/Makefile\n\tsrc/tests/win_sender/Makefile\n\tbuild/Makefile\n\tortp.pc\n\tortp.spec\n\tortp.doxygen\n)\n\n"
  },
  {
    "path": "debian/changelog",
    "content": "ortp (1.0.0) unstable; urgency=low\n  * oRTP license is changed to be GPLv2\n  * new adaptive jitter buffer algorithm added\n\n -- Belledonne Communications <info@belledonne-communications.com>  Wed, 10 Jan 2017 17:36:30 +0200\n\nortp (0.27.0) unstable; urgency=low\n\n -- Belledonne Communications <info@belledonne-communications.com>  Wed, 1 June 2016 16:47:00 +0200\n\nortp (0.26.0) unstable; urgency=low\n  * Fix DSCP on Windows.\n\n -- Belledonne Communications <info@belledonne-communications.com>  Wed, 26 May 2016 14:21:00 +0200\n\nortp (0.25.0) unstable; urgency=low\n  * Suppot AVPF generic NACK\n  * Add payload types for RTT (Real-Time Text) and Codec2\n  * Bug fixes\n\n -- Belledonne Communications <info@belledonne-communications.com>  Mon, 02 Nov 2015 13:54:00 +0200\n\nortp (0.24.2) unstable; urgency=low\n  * Initial package\n\n -- Belledonne Communications <info@belledonne-communications.com>  Tue, 07 Oct 2015 14:44:00 +0200\n\nortp (0.23.0) unstable; urgency=low\n\n  * Network simulator improvements\n  * Security bugfixes\n  * Updated to use ZRTPCPP>=4.0\n\n -- Sylvain Berfini <sylvain.berfini@linphone.org>  Mon, 31 March 2013 15:10:01 +0200\n\nortp (0.22.0) unstable; urgency=low\n\n  * New version + migrate to ABI9.\n  *\n\n -- Guillaume Beraudo <guillaume.beraudo@linphone.org>  Thu, 30 May 2013 15:32:14 +0200\n\nortp (0.21.0) unstable; urgency=low\n\n  * Initial Release.\n\n -- Guillaume Beraudo <guillaume.beraudo@linphone.org>  Fri, 3 May 2012 13:58:16 +0200\n"
  },
  {
    "path": "debian/compat",
    "content": "8\n"
  },
  {
    "path": "debian/control",
    "content": "Source: ortp\nSection: sound\nPriority: optional\nMaintainer: Debian VoIP Team <pkg-voip-maintainers@lists.alioth.debian.org>\nUploaders: Samuel Mimram <smimram@debian.org>, Kilian Krause <kilian@debian.org>, Faidon Liambotis <paravoid@debian.org>, Mark Purcell <msp@debian.org>, Lionel Elie Mamane <lmamane@debian.org>, Tzafrir Cohen <tzafrir@debian.org>\nBuild-Depends: debhelper (>= 8),\n autoconf, automake, autotools-dev, libtool, pkg-config, intltool,\n libglib2.0-dev, \n doxygen\nStandards-Version: 3.9.1\nHomepage: http://www.linphone.org/\nVcs-Svn: svn://svn.debian.org/pkg-voip/linphone/trunk/\nVcs-Browser: http://anonscm.debian.org/viewvc/pkg-voip/linphone/trunk/\n\n\nPackage: libortp13\nSection: libs\nArchitecture: any\nDepends: ${shlibs:Depends}, ${misc:Depends}\nDescription: Real-time Transport Protocol stack\n This library implements the RFC1889 (RTP) with a easy to use API with high\n and low level access.\n .\n Its main features are:\n   - support for multiple profiles, AV profile (RFC 1890) being the default one;\n   - an optional packet scheduler for synchronizing RTP recv and send;\n   - implements blocking and non blocking IO for RTP sessions;\n   - supports multiplexing IO;\n   - supports part of RFC2833 for telephone events over RTP.\n\nPackage: libortp-dev\nSection: libdevel\nArchitecture: any\nDepends: ${misc:Depends}, libortp13 (= ${binary:Version})\nConflicts: libortp7-dev\nDescription: Real-time Transport Protocol stack\n This library implements the RFC1889 (RTP) with a easy to use API with high\n and low level access.\n .\n Its main features are:\n   - support for multiple profiles, AV profile (RFC 1890) being the default one;\n   - an optional packet scheduler for synchronizing RTP recv and send;\n   - implements blocking and non blocking IO for RTP sessions;\n   - supports multiplexing IO;\n   - supports part of RFC2833 for telephone events over RTP.\n\nPackage: libortp13-dbg\nArchitecture: any\nDepends: libortp13 (= ${binary:Version}), ${misc:Depends}\nSection: debug\nPriority: extra\nDescription: Debugging symbols for ortp\n .\n"
  },
  {
    "path": "debian/copyright",
    "content": "This package was debianized by Samuel Mimram <samuel.mimram@ens-lyon.org> on\nWed, 30 Jun 2004 13:58:16 +0200.\n\nIt was downloaded from http://www.linphone.org/\n\nUpstream Authors: Simon Morlat, Florian Wintertein, Aymeric Moizard, Sharath Udupa.\n\nCopyright (C) 2001-2005 Simon Morlat\n\n This program is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\nOn Debian systems you can find a copy of this license in\n/usr/share/common-licenses/GPL.\n\n\nSome libraries are under other copyrights / licenses:\n\n* FFmpeg: Copyright (C) 2000, 2001 Gerard Lantau.\n  Released under the GNU General Public License\n  (see /usr/share/common-licenses/GPL)\n\n* gsmlib: Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,\n  Technische Universitaet Berlin\n\n  Released under the following license:\n\n  Any use of this software is permitted provided that this notice is not\n  removed and that neither the authors nor the Technische Universitaet Berlin\n  are deemed to have made any representations as to the suitability of this\n  software for any purpose nor are held responsible for any defects of\n  this software.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.\n\n  As a matter of courtesy, the authors request to be informed about uses\n  this software has found, about bugs in this software, and about any\n  improvements that may be of general interest.\n\n  Berlin, 28.11.1994\n  Jutta Degener\n  Carsten Bormann\n\n* oRTP: Copyright (C) Simon Morlat\n  Released under the GNU Lesser General Public License\n  (see /usr/share/common-licenses/LGPL).\n\n* osipua: Copyright (C) 2001 Simon Morlat and Aymeric Moizard\n  Released under the GNU Lesser General Public License\n  (see /usr/share/common-licenses/LGPL).\n\n* speex: Copyright (C) 2002-2003 Jean-Marc Valin\n\n  Released under the following license:\n\n  Redistribution and use in source and binary forms, with or without\n  modification, are permitted provided that the following conditions\n  are met:\n\n  - Redistributions of source code must retain the above copyright\n  notice, this list of conditions and the following disclaimer.\n\n  - Redistributions in binary form must reproduce the above copyright\n  notice, this list of conditions and the following disclaimer in the\n  documentation and/or other materials provided with the distribution.\n\n  - Neither the name of the Xiph.org Foundation nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\n  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR\n  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "debian/libortp-dev.docs",
    "content": "usr/share/doc/ortp-*/html/\n"
  },
  {
    "path": "debian/libortp-dev.install",
    "content": "usr/include/ortp/*.h\nusr/lib/libortp.so\nusr/lib/pkgconfig/ortp.pc\nusr/share/doc/ortp-*/html usr/share/doc/libortp13-dev\n"
  },
  {
    "path": "debian/libortp13.docs",
    "content": "AUTHORS\nREADME.md\n"
  },
  {
    "path": "debian/libortp13.install",
    "content": "usr/lib/libortp.so.*\n"
  },
  {
    "path": "debian/rules",
    "content": "#!/usr/bin/make -f\n\n%:\n\tdh $@ --parallel --with autotools_dev\n\noverride_dh_auto_configure:\n\t./configure --prefix=/usr --with-srtp=none\n# As of today, neither flexisip or linphone need the broken srtp\n\noverride_dh_makeshlibs:\n\tdh_makeshlibs -V\n\noverride_dh_installchangelogs:\n\tdh_installchangelogs NEWS\n\noverride_dh_strip:\n\tdh_strip --dbg-package=libortp13-dbg\n"
  },
  {
    "path": "debian/source/format",
    "content": "3.0 (native)\n"
  },
  {
    "path": "debian/source/options",
    "content": "# Don't store changes on autogenerated files\nextend-diff-ignore = \"(^|/)(config\\.sub|config\\.guess|Makefile|m4/.*)$\"\n"
  },
  {
    "path": "debian/watch",
    "content": "version=3\n\nhttp://download.savannah.gnu.org/releases-noredirect/linphone/stable/sources/ linphone-([\\d\\.]+).tar.gz debian svn-upgrade\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "Makefile\nMakefile.in\n"
  },
  {
    "path": "include/.gitignore",
    "content": "Makefile\nMakefile.in\n"
  },
  {
    "path": "include/CMakeLists.txt",
    "content": "############################################################################\n# Copyright (c) 2010-2022 Belledonne Communications SARL.\n#\n# This file is part of oRTP \n# (see https://gitlab.linphone.org/BC/public/ortp).\n#\n############################################################################\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as\n# published by the Free Software Foundation, either version 3 of the\n# License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. If not, see <http://www.gnu.org/licenses/>.\n############################################################################\n\nset(HEADER_FILES\n    event.h\n\tlogging.h\n\tnack.h\n\tortp.h\n\tpayloadtype.h\n\tport.h\n\trtcp.h\n\trtp.h\n\trtpprofile.h\n\trtpsession.h\n\trtpsignaltable.h\n\tsessionset.h\n\tstr_utils.h\n\ttelephonyevents.h\n\tutils.h\n)\n\nset(ORTP_HEADER_FILES )\nforeach(HEADER_FILE ${HEADER_FILES})\n\tlist(APPEND ORTP_HEADER_FILES \"${CMAKE_CURRENT_LIST_DIR}/ortp/${HEADER_FILE}\")\nendforeach()\nset(ORTP_HEADER_FILES ${ORTP_HEADER_FILES} PARENT_SCOPE)\n\ninstall(FILES ${ORTP_HEADER_FILES}\n        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ortp\n        PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)\n"
  },
  {
    "path": "include/MSVC/inttypes.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n// ISO C9x  compliant inttypes.h for Microsoft Visual Studio\n// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124\n//\n//  Copyright (c) 2006 Alexander Chemeris\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are met:\n//\n//   1. Redistributions of source code must retain the above copyright notice,\n//      this list of conditions and the following disclaimer.\n//\n//   2. Redistributions in binary form must reproduce the above copyright\n//      notice, this list of conditions and the following disclaimer in the\n//      documentation and/or other materials provided with the distribution.\n//\n//   3. The name of the author may be used to endorse or promote products\n//      derived from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\n// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n//\n///////////////////////////////////////////////////////////////////////////////\n\n#ifndef _MSC_VER // [\n#error \"Use this header only with Microsoft Visual C++ compilers!\"\n#endif // _MSC_VER ]\n\n#ifndef _MSC_INTTYPES_H_ // [\n#define _MSC_INTTYPES_H_\n\n#if _MSC_VER > 1000\n#pragma once\n#endif\n\n#include \"stdint.h\"\n\n// 7.8 Format conversion of integer types\n\ntypedef struct {\n\tintmax_t quot;\n\tintmax_t rem;\n} imaxdiv_t;\n\n// 7.8.1 Macros for format specifiers\n\n#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [   See footnote 185 at page 198\n\n// The fprintf macros for signed integers are:\n#define PRId8 \"d\"\n#define PRIi8 \"i\"\n#define PRIdLEAST8 \"d\"\n#define PRIiLEAST8 \"i\"\n#define PRIdFAST8 \"d\"\n#define PRIiFAST8 \"i\"\n\n#define PRId16 \"hd\"\n#define PRIi16 \"hi\"\n#define PRIdLEAST16 \"hd\"\n#define PRIiLEAST16 \"hi\"\n#define PRIdFAST16 \"hd\"\n#define PRIiFAST16 \"hi\"\n\n#define PRId32 \"I32d\"\n#define PRIi32 \"I32i\"\n#define PRIdLEAST32 \"I32d\"\n#define PRIiLEAST32 \"I32i\"\n#define PRIdFAST32 \"I32d\"\n#define PRIiFAST32 \"I32i\"\n\n#define PRId64 \"I64d\"\n#define PRIi64 \"I64i\"\n#define PRIdLEAST64 \"I64d\"\n#define PRIiLEAST64 \"I64i\"\n#define PRIdFAST64 \"I64d\"\n#define PRIiFAST64 \"I64i\"\n\n#define PRIdMAX \"I64d\"\n#define PRIiMAX \"I64i\"\n\n#define PRIdPTR \"Id\"\n#define PRIiPTR \"Ii\"\n\n// The fprintf macros for unsigned integers are:\n#define PRIo8 \"o\"\n#define PRIu8 \"u\"\n#define PRIx8 \"x\"\n#define PRIX8 \"X\"\n#define PRIoLEAST8 \"o\"\n#define PRIuLEAST8 \"u\"\n#define PRIxLEAST8 \"x\"\n#define PRIXLEAST8 \"X\"\n#define PRIoFAST8 \"o\"\n#define PRIuFAST8 \"u\"\n#define PRIxFAST8 \"x\"\n#define PRIXFAST8 \"X\"\n\n#define PRIo16 \"ho\"\n#define PRIu16 \"hu\"\n#define PRIx16 \"hx\"\n#define PRIX16 \"hX\"\n#define PRIoLEAST16 \"ho\"\n#define PRIuLEAST16 \"hu\"\n#define PRIxLEAST16 \"hx\"\n#define PRIXLEAST16 \"hX\"\n#define PRIoFAST16 \"ho\"\n#define PRIuFAST16 \"hu\"\n#define PRIxFAST16 \"hx\"\n#define PRIXFAST16 \"hX\"\n\n#define PRIo32 \"I32o\"\n#define PRIu32 \"I32u\"\n#define PRIx32 \"I32x\"\n#define PRIX32 \"I32X\"\n#define PRIoLEAST32 \"I32o\"\n#define PRIuLEAST32 \"I32u\"\n#define PRIxLEAST32 \"I32x\"\n#define PRIXLEAST32 \"I32X\"\n#define PRIoFAST32 \"I32o\"\n#define PRIuFAST32 \"I32u\"\n#define PRIxFAST32 \"I32x\"\n#define PRIXFAST32 \"I32X\"\n\n#define PRIo64 \"I64o\"\n#define PRIu64 \"I64u\"\n#define PRIx64 \"I64x\"\n#define PRIX64 \"I64X\"\n#define PRIoLEAST64 \"I64o\"\n#define PRIuLEAST64 \"I64u\"\n#define PRIxLEAST64 \"I64x\"\n#define PRIXLEAST64 \"I64X\"\n#define PRIoFAST64 \"I64o\"\n#define PRIuFAST64 \"I64u\"\n#define PRIxFAST64 \"I64x\"\n#define PRIXFAST64 \"I64X\"\n\n#define PRIoMAX \"I64o\"\n#define PRIuMAX \"I64u\"\n#define PRIxMAX \"I64x\"\n#define PRIXMAX \"I64X\"\n\n#define PRIoPTR \"Io\"\n#define PRIuPTR \"Iu\"\n#define PRIxPTR \"Ix\"\n#define PRIXPTR \"IX\"\n\n// The fscanf macros for signed integers are:\n#define SCNd8 \"d\"\n#define SCNi8 \"i\"\n#define SCNdLEAST8 \"d\"\n#define SCNiLEAST8 \"i\"\n#define SCNdFAST8 \"d\"\n#define SCNiFAST8 \"i\"\n\n#define SCNd16 \"hd\"\n#define SCNi16 \"hi\"\n#define SCNdLEAST16 \"hd\"\n#define SCNiLEAST16 \"hi\"\n#define SCNdFAST16 \"hd\"\n#define SCNiFAST16 \"hi\"\n\n#define SCNd32 \"ld\"\n#define SCNi32 \"li\"\n#define SCNdLEAST32 \"ld\"\n#define SCNiLEAST32 \"li\"\n#define SCNdFAST32 \"ld\"\n#define SCNiFAST32 \"li\"\n\n#define SCNd64 \"I64d\"\n#define SCNi64 \"I64i\"\n#define SCNdLEAST64 \"I64d\"\n#define SCNiLEAST64 \"I64i\"\n#define SCNdFAST64 \"I64d\"\n#define SCNiFAST64 \"I64i\"\n\n#define SCNdMAX \"I64d\"\n#define SCNiMAX \"I64i\"\n\n#ifdef _WIN64 // [\n#define SCNdPTR \"I64d\"\n#define SCNiPTR \"I64i\"\n#else // _WIN64 ][\n#define SCNdPTR \"ld\"\n#define SCNiPTR \"li\"\n#endif // _WIN64 ]\n\n// The fscanf macros for unsigned integers are:\n#define SCNo8 \"o\"\n#define SCNu8 \"u\"\n#define SCNx8 \"x\"\n#define SCNX8 \"X\"\n#define SCNoLEAST8 \"o\"\n#define SCNuLEAST8 \"u\"\n#define SCNxLEAST8 \"x\"\n#define SCNXLEAST8 \"X\"\n#define SCNoFAST8 \"o\"\n#define SCNuFAST8 \"u\"\n#define SCNxFAST8 \"x\"\n#define SCNXFAST8 \"X\"\n\n#define SCNo16 \"ho\"\n#define SCNu16 \"hu\"\n#define SCNx16 \"hx\"\n#define SCNX16 \"hX\"\n#define SCNoLEAST16 \"ho\"\n#define SCNuLEAST16 \"hu\"\n#define SCNxLEAST16 \"hx\"\n#define SCNXLEAST16 \"hX\"\n#define SCNoFAST16 \"ho\"\n#define SCNuFAST16 \"hu\"\n#define SCNxFAST16 \"hx\"\n#define SCNXFAST16 \"hX\"\n\n#define SCNo32 \"lo\"\n#define SCNu32 \"lu\"\n#define SCNx32 \"lx\"\n#define SCNX32 \"lX\"\n#define SCNoLEAST32 \"lo\"\n#define SCNuLEAST32 \"lu\"\n#define SCNxLEAST32 \"lx\"\n#define SCNXLEAST32 \"lX\"\n#define SCNoFAST32 \"lo\"\n#define SCNuFAST32 \"lu\"\n#define SCNxFAST32 \"lx\"\n#define SCNXFAST32 \"lX\"\n\n#define SCNo64 \"I64o\"\n#define SCNu64 \"I64u\"\n#define SCNx64 \"I64x\"\n#define SCNX64 \"I64X\"\n#define SCNoLEAST64 \"I64o\"\n#define SCNuLEAST64 \"I64u\"\n#define SCNxLEAST64 \"I64x\"\n#define SCNXLEAST64 \"I64X\"\n#define SCNoFAST64 \"I64o\"\n#define SCNuFAST64 \"I64u\"\n#define SCNxFAST64 \"I64x\"\n#define SCNXFAST64 \"I64X\"\n\n#define SCNoMAX \"I64o\"\n#define SCNuMAX \"I64u\"\n#define SCNxMAX \"I64x\"\n#define SCNXMAX \"I64X\"\n\n#ifdef _WIN64 // [\n#define SCNoPTR \"I64o\"\n#define SCNuPTR \"I64u\"\n#define SCNxPTR \"I64x\"\n#define SCNXPTR \"I64X\"\n#else // _WIN64 ][\n#define SCNoPTR \"lo\"\n#define SCNuPTR \"lu\"\n#define SCNxPTR \"lx\"\n#define SCNXPTR \"lX\"\n#endif // _WIN64 ]\n\n#endif // __STDC_FORMAT_MACROS ]\n\n// 7.8.2 Functions for greatest-width integer types\n\n// 7.8.2.1 The imaxabs function\n#define imaxabs _abs64\n\n// 7.8.2.2 The imaxdiv function\n\n// This is modified version of div() function from Microsoft's div.c found\n// in %MSVC.NET%\\crt\\src\\div.c\n#ifdef STATIC_IMAXDIV // [\nstatic\n#else  // STATIC_IMAXDIV ][\n_inline\n#endif // STATIC_IMAXDIV ]\n    imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) {\n\timaxdiv_t result;\n\n\tresult.quot = numer / denom;\n\tresult.rem = numer % denom;\n\n\tif (numer < 0 && result.rem > 0) {\n\t\t// did division wrong; must fix up\n\t\t++result.quot;\n\t\tresult.rem -= denom;\n\t}\n\n\treturn result;\n}\n\n// 7.8.2.3 The strtoimax and strtoumax functions\n#define strtoimax _strtoi64\n#define strtoumax _strtoui64\n\n// 7.8.2.4 The wcstoimax and wcstoumax functions\n#define wcstoimax _wcstoi64\n#define wcstoumax _wcstoui64\n\n#endif // _MSC_INTTYPES_H_ ]\n"
  },
  {
    "path": "include/MSVC/stdint.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n// ISO C9x  compliant stdint.h for Microsoft Visual Studio\n// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124\n//\n//  Copyright (c) 2006-2008 Alexander Chemeris\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are met:\n//\n//   1. Redistributions of source code must retain the above copyright notice,\n//      this list of conditions and the following disclaimer.\n//\n//   2. Redistributions in binary form must reproduce the above copyright\n//      notice, this list of conditions and the following disclaimer in the\n//      documentation and/or other materials provided with the distribution.\n//\n//   3. The name of the author may be used to endorse or promote products\n//      derived from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\n// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\n// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n//\n///////////////////////////////////////////////////////////////////////////////\n\n#ifndef _MSC_VER // [\n#error \"Use this header only with Microsoft Visual C++ compilers!\"\n#endif // _MSC_VER ]\n\n#ifndef _MSC_STDINT_H_ // [\n#define _MSC_STDINT_H_\n\n#if _MSC_VER > 1000\n#pragma once\n#endif\n\n#include <limits.h>\n\n// For Visual Studio 6 in C++ mode and for many Visual Studio versions when\n// compiling for ARM we should wrap <wchar.h> include with 'extern \"C++\" {}'\n// or compiler give many errors like this:\n//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#include <wchar.h>\n#ifdef __cplusplus\n}\n#endif\n\n// Define _W64 macros to mark types changing their size, like intptr_t.\n#ifndef _W64\n#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300\n#define _W64 __w64\n#else\n#define _W64\n#endif\n#endif\n\n// 7.18.1 Integer types\n\n// 7.18.1.1 Exact-width integer types\n\n// Visual Studio 6 and Embedded Visual C++ 4 doesn't\n// realize that, e.g. char has the same size as __int8\n// so we give up on __intX for them.\n#if (_MSC_VER < 1300)\ntypedef signed char int8_t;\ntypedef signed short int16_t;\ntypedef signed int int32_t;\ntypedef unsigned char uint8_t;\ntypedef unsigned short uint16_t;\ntypedef unsigned int uint32_t;\n#else\ntypedef signed __int8 int8_t;\ntypedef signed __int16 int16_t;\ntypedef signed __int32 int32_t;\ntypedef unsigned __int8 uint8_t;\ntypedef unsigned __int16 uint16_t;\ntypedef unsigned __int32 uint32_t;\n#endif\ntypedef signed __int64 int64_t;\ntypedef unsigned __int64 uint64_t;\n\n// 7.18.1.2 Minimum-width integer types\ntypedef int8_t int_least8_t;\ntypedef int16_t int_least16_t;\ntypedef int32_t int_least32_t;\ntypedef int64_t int_least64_t;\ntypedef uint8_t uint_least8_t;\ntypedef uint16_t uint_least16_t;\ntypedef uint32_t uint_least32_t;\ntypedef uint64_t uint_least64_t;\n\n// 7.18.1.3 Fastest minimum-width integer types\ntypedef int8_t int_fast8_t;\ntypedef int16_t int_fast16_t;\ntypedef int32_t int_fast32_t;\ntypedef int64_t int_fast64_t;\ntypedef uint8_t uint_fast8_t;\ntypedef uint16_t uint_fast16_t;\ntypedef uint32_t uint_fast32_t;\ntypedef uint64_t uint_fast64_t;\n\n// 7.18.1.4 Integer types capable of holding object pointers\n#ifdef _WIN64 // [\ntypedef signed __int64 intptr_t;\ntypedef unsigned __int64 uintptr_t;\n#else  // _WIN64 ][\ntypedef _W64 signed int intptr_t;\ntypedef _W64 unsigned int uintptr_t;\n#endif // _WIN64 ]\n\n// 7.18.1.5 Greatest-width integer types\ntypedef int64_t intmax_t;\ntypedef uint64_t uintmax_t;\n\n// 7.18.2 Limits of specified-width integer types\n\n#if !defined(__cplusplus) ||                                                                                           \\\n    defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259\n\n// 7.18.2.1 Limits of exact-width integer types\n#define INT8_MIN ((int8_t)_I8_MIN)\n#define INT8_MAX _I8_MAX\n#define INT16_MIN ((int16_t)_I16_MIN)\n#define INT16_MAX _I16_MAX\n#define INT32_MIN ((int32_t)_I32_MIN)\n#define INT32_MAX _I32_MAX\n#define INT64_MIN ((int64_t)_I64_MIN)\n#define INT64_MAX _I64_MAX\n#define UINT8_MAX _UI8_MAX\n#define UINT16_MAX _UI16_MAX\n#define UINT32_MAX _UI32_MAX\n#define UINT64_MAX _UI64_MAX\n\n// 7.18.2.2 Limits of minimum-width integer types\n#define INT_LEAST8_MIN INT8_MIN\n#define INT_LEAST8_MAX INT8_MAX\n#define INT_LEAST16_MIN INT16_MIN\n#define INT_LEAST16_MAX INT16_MAX\n#define INT_LEAST32_MIN INT32_MIN\n#define INT_LEAST32_MAX INT32_MAX\n#define INT_LEAST64_MIN INT64_MIN\n#define INT_LEAST64_MAX INT64_MAX\n#define UINT_LEAST8_MAX UINT8_MAX\n#define UINT_LEAST16_MAX UINT16_MAX\n#define UINT_LEAST32_MAX UINT32_MAX\n#define UINT_LEAST64_MAX UINT64_MAX\n\n// 7.18.2.3 Limits of fastest minimum-width integer types\n#define INT_FAST8_MIN INT8_MIN\n#define INT_FAST8_MAX INT8_MAX\n#define INT_FAST16_MIN INT16_MIN\n#define INT_FAST16_MAX INT16_MAX\n#define INT_FAST32_MIN INT32_MIN\n#define INT_FAST32_MAX INT32_MAX\n#define INT_FAST64_MIN INT64_MIN\n#define INT_FAST64_MAX INT64_MAX\n#define UINT_FAST8_MAX UINT8_MAX\n#define UINT_FAST16_MAX UINT16_MAX\n#define UINT_FAST32_MAX UINT32_MAX\n#define UINT_FAST64_MAX UINT64_MAX\n\n// 7.18.2.4 Limits of integer types capable of holding object pointers\n#ifdef _WIN64 // [\n#define INTPTR_MIN INT64_MIN\n#define INTPTR_MAX INT64_MAX\n#define UINTPTR_MAX UINT64_MAX\n#else // _WIN64 ][\n#define INTPTR_MIN INT32_MIN\n#define INTPTR_MAX INT32_MAX\n#define UINTPTR_MAX UINT32_MAX\n#endif // _WIN64 ]\n\n// 7.18.2.5 Limits of greatest-width integer types\n#define INTMAX_MIN INT64_MIN\n#define INTMAX_MAX INT64_MAX\n#define UINTMAX_MAX UINT64_MAX\n\n// 7.18.3 Limits of other integer types\n\n#ifdef _WIN64 // [\n#define PTRDIFF_MIN _I64_MIN\n#define PTRDIFF_MAX _I64_MAX\n#else // _WIN64 ][\n#define PTRDIFF_MIN _I32_MIN\n#define PTRDIFF_MAX _I32_MAX\n#endif // _WIN64 ]\n\n#define SIG_ATOMIC_MIN INT_MIN\n#define SIG_ATOMIC_MAX INT_MAX\n\n#ifndef SIZE_MAX // [\n#ifdef _WIN64    // [\n#define SIZE_MAX _UI64_MAX\n#else // _WIN64 ][\n#define SIZE_MAX _UI32_MAX\n#endif // _WIN64 ]\n#endif // SIZE_MAX ]\n\n// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>\n#ifndef WCHAR_MIN // [\n#define WCHAR_MIN 0\n#endif            // WCHAR_MIN ]\n#ifndef WCHAR_MAX // [\n#define WCHAR_MAX _UI16_MAX\n#endif // WCHAR_MAX ]\n\n#define WINT_MIN 0\n#define WINT_MAX _UI16_MAX\n\n#endif // __STDC_LIMIT_MACROS ]\n\n// 7.18.4 Limits of other integer types\n\n#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260\n\n// 7.18.4.1 Macros for minimum-width integer constants\n\n#define INT8_C(val) val##i8\n#define INT16_C(val) val##i16\n#define INT32_C(val) val##i32\n#define INT64_C(val) val##i64\n\n#define UINT8_C(val) val##ui8\n#define UINT16_C(val) val##ui16\n#define UINT32_C(val) val##ui32\n#define UINT64_C(val) val##ui64\n\n// 7.18.4.2 Macros for greatest-width integer constants\n#define INTMAX_C INT64_C\n#define UINTMAX_C UINT64_C\n\n#endif // __STDC_CONSTANT_MACROS ]\n\n#endif // _MSC_STDINT_H_ ]\n"
  },
  {
    "path": "include/Makefile.am",
    "content": "SUBDIRS=ortp\n"
  },
  {
    "path": "include/ortp/.gitignore",
    "content": "Makefile\nMakefile.in\n"
  },
  {
    "path": "include/ortp/Makefile.am",
    "content": "ortp_includedir=$(includedir)/ortp\n\nortp_include_HEADERS=str_utils.h rtpsession.h rtp.h port.h logging.h nack.h \\\n\t\t\t\t\tortp.h telephonyevents.h sessionset.h payloadtype.h rtpprofile.h rtpsignaltable.h \\\n\t\t\t\t\trtcp.h event.h utils.h \\\n\t\t\t\t\tb64.h\n\nEXTRA_DIST=$(ortp_include_HEADERS)\n"
  },
  {
    "path": "include/ortp/event.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef ortp_events_h\n#define ortp_events_h\n\n#include \"bctoolbox/list.h\"\n#include \"ortp/port.h\"\n#include \"ortp/rtcp.h\"\n#include \"ortp/str_utils.h\"\n\ntypedef mblk_t OrtpEvent;\n\ntypedef unsigned long OrtpEventType;\n\ntypedef enum { OrtpRTPSocket, OrtpRTCPSocket } OrtpSocketType;\n\nstruct _OrtpEventData {\n\tmblk_t *packet; /* most events are associated to a received packet */\n\tstruct sockaddr_storage source_addr;\n\tsocklen_t source_addrlen;\n\tortpTimeSpec ts;\n\tunion {\n\t\tint sequence_number_diff;\n\t\tint telephone_event;\n\t\tint payload_type;\n\t\tbool_t dtls_stream_encrypted;\n\t\tbool_t zrtp_stream_encrypted;\n\t\tbool_t ice_processing_successful;\n\t\tstruct _zrtp_info {\n\t\t\tchar sas[32];              // up to 31 + null characters\n\t\t\tchar incorrect_sas[3][32]; // List of 3 half bad SAS. Up to 31 + null characters\n\t\t\tbool_t verified;\n\t\t\tbool_t cache_mismatch;\n\t\t\tbool_t pad[2];\n\t\t\tint cipherAlgo;\n\t\t\tint keyAgreementAlgo;\n\t\t\tint hashAlgo;\n\t\t\tint authTagAlgo;\n\t\t\tint sasAlgo;\n\t\t} zrtp_info;\n\t\tstruct _srtp_info {\n\t\t\tbool_t is_send;  /**< stream direction this is applied too */\n\t\t\tbool_t is_inner; /**< this info applies to inner encryption (in case of SRTP double encryption) */\n\t\t\tint source;      /**< the source of the key material as defined in MSSrtpKeySource enum in ms_strp.h */\n\t\t\tint suite;       /**< the srtp crypto suite used as defined in MSCryptoSuite enum in ms_srtp.h */\n\t\t} srtp_info;\n\t\tOrtpSocketType socket_type;\n\t\tuint32_t received_rtt_character;\n\t\tbool_t congestion_detected;\n\t\tfloat video_bandwidth_available;\n\t\tfloat audio_bandwidth_available;\n\t\tint jitter_min_size_for_nack;\n\t} info;\n};\n\ntypedef struct _OrtpEventData OrtpEventData;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nORTP_PUBLIC OrtpEvent *ortp_event_new(OrtpEventType tp);\nORTP_PUBLIC OrtpEventType ortp_event_get_type(const OrtpEvent *ev);\n/* type is one of the following*/\n\n#define ORTP_EVENT_STUN_PACKET_RECEIVED 1\n#define ORTP_EVENT_PAYLOAD_TYPE_CHANGED 2\n#define ORTP_EVENT_TELEPHONE_EVENT 3\n#define ORTP_EVENT_RTCP_PACKET_RECEIVED 4 /**<when a RTCP packet is received from far end */\n#define ORTP_EVENT_RTCP_PACKET_EMITTED 5  /**<fired when oRTP decides to send an automatic RTCP SR or RR */\n#define ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED 6\n#define ORTP_EVENT_ZRTP_SAS_READY 7\n#define ORTP_EVENT_ICE_CHECK_LIST_PROCESSING_FINISHED 8\n#define ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED 9\n#define ORTP_EVENT_ICE_GATHERING_FINISHED 10\n#define ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED 11\n#define ORTP_EVENT_ICE_RESTART_NEEDED 12\n#define ORTP_EVENT_ICE_CHECK_LIST_DEFAULT_CANDIDATE_VERIFIED 25\n#define ORTP_EVENT_DTLS_ENCRYPTION_CHANGED 13\n#define ORTP_EVENT_RTT_CHARACTER_RECEIVED 15\n#define ORTP_EVENT_CONGESTION_STATE_CHANGED 16\n#define ORTP_EVENT_ZRTP_CACHE_MISMATCH 17\n#define ORTP_EVENT_ZRTP_PEER_VERSION_OBSOLETE 18\n#define ORTP_EVENT_ZRTP_PEER_REQUEST_GOCLEAR 19\n#define ORTP_EVENT_ZRTP_PEER_ACK_GOCLEAR 20\n#define ORTP_EVENT_NEW_VIDEO_BANDWIDTH_ESTIMATION_AVAILABLE 21\n#define ORTP_EVENT_ICE_DEACTIVATION_NEEDED 22\n#define ORTP_EVENT_JITTER_UPDATE_FOR_NACK 23\n#define ORTP_EVENT_DO_NOT_USE_RESERVED 25 /* taken by ORTP_EVENT_ICE_CHECK_LIST_DEFAULT_CANDIDATE_VERIFIED */\n#define ORTP_EVENT_SRTP_ENCRYPTION_CHANGED                                                                             \\\n\t26 /* srtp status changed - set key material source and crypto suite usesd in stream stats */\n#define ORTP_EVENT_BURST_OCCURED 27\n#define ORTP_EVENT_NEW_AUDIO_BANDWIDTH_ESTIMATION_AVAILABLE 28\n\nORTP_PUBLIC OrtpEventData *ortp_event_get_data(OrtpEvent *ev);\nORTP_PUBLIC void ortp_event_destroy(OrtpEvent *ev);\nORTP_PUBLIC OrtpEvent *ortp_event_dup(OrtpEvent *ev);\n\ntypedef struct OrtpEvQueue {\n\tqueue_t q;\n\tortp_mutex_t mutex;\n} OrtpEvQueue;\n\nORTP_PUBLIC OrtpEvQueue *ortp_ev_queue_new(void);\nORTP_PUBLIC void ortp_ev_queue_destroy(OrtpEvQueue *q);\nORTP_PUBLIC OrtpEvent *ortp_ev_queue_get(OrtpEvQueue *q);\nORTP_PUBLIC void ortp_ev_queue_flush(OrtpEvQueue *qp);\n\nstruct _RtpSession;\n\n/**\n * Callback function when a RTCP packet of the interested type is found.\n *\n * @param evd the packet. Read-only, must NOT be changed.\n * @param user_data user data provided when registered the callback\n *\n */\ntypedef void (*OrtpEvDispatcherCb)(const OrtpEventData *evd, void *user_data);\ntypedef struct OrtpEvDispatcherData {\n\tOrtpEventType type;\n\trtcp_type_t subtype;\n\tOrtpEvDispatcherCb on_found;\n\tvoid *user_data;\n} OrtpEvDispatcherData;\n\ntypedef struct OrtpEvDispatcher {\n\tOrtpEvQueue *q;\n\tstruct _RtpSession *session;\n\tbctbx_list_t *cbs;\n} OrtpEvDispatcher;\n\n/**\n * Constructs an OrtpEvDispatcher object. This object can be used to be notified\n * when any RTCP type packet is received or emitted on the rtp session,\n * given a callback registered with \\a ortp_ev_dispatcher_connect\n *\n * @param session RTP session to listen on. Cannot be NULL.\n *\n * @return OrtpEvDispatcher object newly created.\n */\nORTP_PUBLIC OrtpEvDispatcher *ortp_ev_dispatcher_new(struct _RtpSession *session);\n/**\n * Frees the memory for the given dispatcher. Note that user_data must be freed\n * by caller, and so does the OrtpEvQueue.\n *\n * @param d OrtpEvDispatcher object\n */\nORTP_PUBLIC void ortp_ev_dispatcher_destroy(OrtpEvDispatcher *d);\n/**\n * Iterate method to be called periodically. If a RTCP packet is found and\n * its type matches one of the callback connected with \\a ortp_ev_dispatcher_connect,\n * this callback will be invoked in the current thread.\n *\n * @param d OrtpEvDispatcher object\n */\nORTP_PUBLIC void ortp_ev_dispatcher_iterate(OrtpEvDispatcher *d);\n/**\n * Connects a callback to the given type of event packet and, in case of RTCP event,\n * for the given RTCP subtype.\n * When any event is found, the callback is invoked. Multiple\n * callbacks can be connected to the same type of event, and the same callback\n * can be connected to multiple type of event.\n *\n * @param d OrtpEvDispatcher object\n * @param type type of event to be notified of.\n * @param subtype when type is set to ORTP_EVENT_RTCP_PACKET_RECEIVED or ORTP_EVENT_RTCP_PACKET_EMITTED, subtype of RTCP\n * packet to be notified of. Otherwise this parameter is not used.\n * @param on_receive function to call when a RTCP packet of the given type is found.\n * @param user_data user data given as last argument of the callback. Can be NULL. MUST be freed by user.\n */\nORTP_PUBLIC void ortp_ev_dispatcher_connect(\n    OrtpEvDispatcher *d, OrtpEventType type, rtcp_type_t subtype, OrtpEvDispatcherCb on_receive, void *user_data);\n\n/**\n * Disconnects the given callback for the given type and subtype on the given dispatcher.\n */\nORTP_PUBLIC void\nortp_ev_dispatcher_disconnect(OrtpEvDispatcher *d, OrtpEventType type, rtcp_type_t subtype, OrtpEvDispatcherCb cb);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/logging.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/**\n * \\file logging.h\n * \\brief Logging API.\n *\n **/\n\n#ifndef ORTP_LOGGING_H\n#define ORTP_LOGGING_H\n\n#include <ortp/port.h>\n\n#define ORTP_LOG_DOMAIN BCTBX_LOG_DOMAIN\n\n#include \"bctoolbox/logging.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/***************/\n/* logging api */\n/***************/\n\n#define ORTP_FATAL BCTBX_LOG_FATAL\n#define ORTP_ERROR BCTBX_LOG_ERROR\n#define ORTP_WARNING BCTBX_LOG_WARNING\n#define ORTP_MESSAGE BCTBX_LOG_MESSAGE\n#define ORTP_TRACE BCTBX_LOG_TRACE\n#define ORTP_DEBUG BCTBX_LOG_DEBUG\n#define ORTP_END BCTBX_LOG_END\n#define ORTP_LOGLEV_END BCTBX_LOG_LOGLEV_END\n#define OrtpLogLevel BctbxLogLevel\n\n#define OrtpLogFunc BctbxLogFunc\n\n/*#define ortp_set_log_file bctbx_set_log_file*/\nORTP_PUBLIC void ortp_set_log_file(FILE *file);\n\n/*#define ortp_set_log_handler bctbx_set_log_handler*/\nORTP_PUBLIC void ortp_set_log_handler(OrtpLogFunc func);\n\n/* This function does not have any means by now, as even bctbx_set_log_handler is deprecated. use bctbx_log_handler_t\n * instead*/\nORTP_PUBLIC OrtpLogFunc ortp_get_log_handler(void);\n\n#define ortp_logv_out bctbx_logv_out\n/*ORTP_PUBLIC void ortp_logv_out(const char *domain, OrtpLogLevel level, const char *fmt, va_list args);*/\n\n#define ortp_log_level_enabled(domain, level) (bctbx_get_log_level_mask(domain) & (level))\n#define ortp_logv bctbx_logv\n/*ORTP_PUBLIC void ortp_logv(const char *domain, OrtpLogLevel level, const char *fmt, va_list args);*/\n\n/**\n * Flushes the log output queue.\n * WARNING: Must be called from the thread that has been defined with ortp_set_log_thread_id().\n */\n#define ortp_logv_flush bctbx_logv_flush\n/*ORTP_PUBLIC void ortp_logv_flush(void);*/\n\n/**\n * Activate all log level greater or equal than specified level argument.\n **/\n#define ortp_set_log_level bctbx_set_log_level\n/*ORTP_PUBLIC void ortp_set_log_level(const char *domain, OrtpLogLevel level);*/\n\n#define ortp_set_log_level_mask bctbx_set_log_level_mask\n/*ORTP_PUBLIC void ortp_set_log_level_mask(const char *domain, int levelmask);*/\n#define ortp_get_log_level_mask bctbx_get_log_level_mask\n/*ORTP_PUBLIC unsigned int ortp_get_log_level_mask(const char *domain);*/\n\n/**\n * Tell oRTP the id of the thread used to output the logs.\n * This is meant to output all the logs from the same thread to prevent deadlock problems at the application level.\n */\n#define ortp_set_log_thread_id(id) bctbx_set_log_thread_id(id)\n/*ORTP_PUBLIC void ortp_set_log_thread_id(unsigned long thread_id);*/\n\n#ifdef ORTP_DEBUG_MODE\n#define ortp_debug bctbx_debug\n#else\n\n#define ortp_debug(...)\n\n#endif\n\n#ifdef ORTP_NOMESSAGE_MODE\n\n#define ortp_log(...)\n#define ortp_message(...)\n#define ortp_warning(...)\n\n#else\n\nstatic ORTP_INLINE void CHECK_FORMAT_ARGS(2, 3) ortp_log(OrtpLogLevel lev, const char *fmt, ...) {\n\tva_list args;\n\tva_start(args, fmt);\n\tbctbx_logv(ORTP_LOG_DOMAIN, lev, fmt, args);\n\tva_end(args);\n}\n\n#define ortp_message bctbx_message\n#define ortp_warning bctbx_warning\n#define ortp_error bctbx_error\n#define ortp_fatal bctbx_fatal\n#endif /*ORTP_NOMESSAGE_MODE*/\n\n#ifdef __QNX__\n#define ortp_qnx_log_handler bctbx_qnx_log_handler\n/*void ortp_qnx_log_handler(const char *domain, OrtpLogLevel lev, const char *fmt, va_list args);*/\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/nack.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef NACK_H\n#define NACK_H\n\n#include <bctoolbox/list.h>\n#include <bctoolbox/port.h>\n#include <ortp/port.h>\n#include <ortp/rtpsession.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct _OrtpNackContext {\n\tRtpSession *session;\n\tOrtpEvDispatcher *ev_dispatcher;\n\tRtpTransportModifier *rtp_modifier;\n\tRtpTransportModifier *rtcp_modifier;\n\tqueue_t sent_packets;\n\tbctbx_mutex_t sent_packets_mutex;\n\tint max_packets;\n\tint min_jitter_before_nack;\n\tbool_t decrease_jitter_timer_running;\n\tuint64_t decrease_jitter_timer_start;\n\tuint64_t cum_packet_loss;\n\tuint64_t loss_before_nack;\n};\n\ntypedef struct _OrtpNackContext OrtpNackContext;\n\nORTP_PUBLIC OrtpNackContext *ortp_nack_context_new(OrtpEvDispatcher *evt);\nORTP_PUBLIC void ortp_nack_context_destroy(OrtpNackContext *ctx);\n\nORTP_PUBLIC void ortp_nack_context_set_max_packet(OrtpNackContext *ctx, int max);\n\nORTP_PUBLIC void ortp_nack_context_process_timer(OrtpNackContext *ctx);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/ortp.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/** \\mainpage oRTP API documentation\n *\n * \\section init Initializing oRTP\n *\n * see ortp.h documentation.\n *\n * \\section rtpsession the RtpSession object\n *\n * see the rtpsession.h documentation.\n *\n * \\section payloadtypes Managing PayloadType(s) and RtpProfile(s)\n *\n * see the payloadtype.h documentation.\n *\n * \\section telephonevents Sending and receiving telephone-event (RFC2833)\n *\n * see the telephonyevents.h documentation.\n * To get informed about incoming telephone-event you can register a callback\n * using rtp_session_signal_connect() or by registering an event queue using\n * rtp_session_register_event_queue().\n *\n * \\section sessionset Managing several RtpSession simultaneously\n *\n * see the sessionset.h documentation.\n *\n * \\section rtcp Parsing incoming rtcp packets.\n *\n * The parsing api is defined in rtcp.h (not yet documented).\n *\n * \\section examples Examples\n *\n * oRTP comes with a set of examples in src/tests.\n * - rtprecv.c rtpsend.c show how to receive and send a single RTP stream.\n * - mrtprecv.c mrtpsend.c show how to receive and send multiple RTP streams\n *   simultaneously\n *\n */\n\n/**\n * \\file ortp.h\n * \\brief General purpose library functions.\n *\n **/\n\n#ifndef ORTP_H\n#define ORTP_H\n#include \"ortp/logging.h\"\n#include \"ortp/rtpsession.h\"\n#include \"ortp/sessionset.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nORTP_PUBLIC bool_t ortp_min_version_required(int major, int minor, int micro);\nORTP_PUBLIC void ortp_init(void);\nORTP_PUBLIC void ortp_scheduler_init(void);\nORTP_PUBLIC void ortp_exit(void);\n\n/****************/\n/*statistics api*/\n/****************/\n\nextern rtp_stats_t ortp_global_stats;\n\nORTP_PUBLIC void ortp_global_stats_reset(void);\nORTP_PUBLIC rtp_stats_t *ortp_get_global_stats(void);\n\nORTP_PUBLIC void ortp_global_stats_display(void);\nORTP_PUBLIC void rtp_stats_display(const rtp_stats_t *stats, const char *header);\nORTP_PUBLIC void rtp_stats_display_all(const rtp_stats_t *stats1, const rtp_stats_t *stats2, const char *header);\nORTP_PUBLIC void rtp_stats_reset(rtp_stats_t *stats);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/payloadtype.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/**\n * \\file payloadtype.h\n * \\brief Definition of payload types\n *\n **/\n\n#ifndef PAYLOADTYPE_H\n#define PAYLOADTYPE_H\n#include <ortp/port.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* flags for PayloadType::flags */\n\n#define PAYLOAD_TYPE_ALLOCATED (1)\n/*payload type represents a VBR codec*/\n#define PAYLOAD_TYPE_IS_VBR (1 << 1)\n#define PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED (1 << 2)\n/* private flags for future use by ortp */\n#define PAYLOAD_TYPE_PRIV1 (1 << 3)\n/* user flags, can be used by the application on top of oRTP */\n#define PAYLOAD_TYPE_USER_FLAG_0 (1 << 4)\n#define PAYLOAD_TYPE_USER_FLAG_1 (1 << 5)\n#define PAYLOAD_TYPE_USER_FLAG_2 (1 << 6)\n#define PAYLOAD_TYPE_USER_FLAG_3 (1 << 7)\n#define PAYLOAD_TYPE_USER_FLAG_4 (1 << 8)\n#define PAYLOAD_TYPE_USER_FLAG_5 (1 << 9)\n#define PAYLOAD_TYPE_USER_FLAG_6 (1 << 10)\n#define PAYLOAD_TYPE_USER_FLAG_7 (1 << 11)\n/* ask for more if you need*/\n\n#define PAYLOAD_TYPE_FLAG_CAN_RECV PAYLOAD_TYPE_USER_FLAG_1\n#define PAYLOAD_TYPE_FLAG_CAN_SEND PAYLOAD_TYPE_USER_FLAG_2\n\n#define PAYLOAD_AUDIO_CONTINUOUS 0\n#define PAYLOAD_AUDIO_PACKETIZED 1\n#define PAYLOAD_VIDEO 2\n#define PAYLOAD_TEXT 3\n#define PAYLOAD_OTHER 4 /* ?? */\n\n#define PAYLOAD_TYPE_AVPF_NONE 0\n#define PAYLOAD_TYPE_AVPF_FIR (1 << 0)\n#define PAYLOAD_TYPE_AVPF_PLI (1 << 1)\n#define PAYLOAD_TYPE_AVPF_SLI (1 << 2)\n#define PAYLOAD_TYPE_AVPF_RPSI (1 << 3)\n\nstruct _PayloadTypeAvpfParams {\n\tunsigned char features;    /**< A bitmask of PAYLOAD_TYPE_AVPF_* macros. */\n\tbool_t rpsi_compatibility; /*< Linphone uses positive feeback for RPSI. However first versions handling\n\t    AVPF wrongly declared RPSI as negative feedback, so this is kept for compatibility\n\t    with these versions but will probably be removed at some point in time. */\n\tuint16_t trr_interval;     /**< The interval in milliseconds between regular RTCP packets. */\n};\n\nstruct _OrtpPayloadType {\n\tint type;             /**< one of PAYLOAD_* macros*/\n\tint clock_rate;       /**< rtp clock rate*/\n\tchar bits_per_sample; /* in case of continuous audio data */\n\tchar *zero_pattern;\n\tint pattern_length;\n\t/* other useful information for the application*/\n\tint normal_bitrate;                 /*in bit/s */\n\tchar *mime_type;                    /**<actually the submime, ex: pcm, pcma, gsm*/\n\tint channels;                       /**< number of channels of audio */\n\tchar *recv_fmtp;                    /* various format parameters for the incoming stream */\n\tchar *send_fmtp;                    /* various format parameters for the outgoing stream */\n\tstruct _PayloadTypeAvpfParams avpf; /* AVPF parameters */\n\tint flags;\n\tvoid *user_data;\n};\n\n#ifndef PayloadType_defined\n#define PayloadType_defined\ntypedef struct _OrtpPayloadType OrtpPayloadType;\ntypedef OrtpPayloadType PayloadType;\ntypedef struct _PayloadTypeAvpfParams PayloadTypeAvpfParams;\n#endif\n\n#define payload_type_set_flag(pt, flag) (pt)->flags |= ((int)flag)\n#define payload_type_unset_flag(pt, flag) (pt)->flags &= (~(int)flag)\n#define payload_type_get_flags(pt) (pt)->flags\n\nORTP_PUBLIC PayloadType *payload_type_new(void);\nORTP_PUBLIC PayloadType *payload_type_clone(const PayloadType *payload);\nORTP_PUBLIC char *payload_type_get_rtpmap(PayloadType *pt);\nORTP_PUBLIC void payload_type_destroy(PayloadType *pt);\nORTP_PUBLIC void payload_type_set_recv_fmtp(PayloadType *pt, const char *fmtp);\nORTP_PUBLIC void payload_type_set_send_fmtp(PayloadType *pt, const char *fmtp);\nORTP_PUBLIC void payload_type_append_recv_fmtp(PayloadType *pt, const char *fmtp);\nORTP_PUBLIC void payload_type_append_send_fmtp(PayloadType *pt, const char *fmtp);\n#define payload_type_get_avpf_params(pt) ((pt)->avpf)\nORTP_PUBLIC void payload_type_set_avpf_params(PayloadType *pt, PayloadTypeAvpfParams params);\nORTP_PUBLIC bool_t payload_type_is_vbr(const PayloadType *pt);\n\n#define payload_type_get_bitrate(pt) ((pt)->normal_bitrate)\n#define payload_type_get_rate(pt) ((pt)->clock_rate)\n#define payload_type_get_mime(pt) ((pt)->mime_type)\n\nORTP_PUBLIC bool_t fmtp_get_value(const char *fmtp, const char *param_name, char *result, size_t result_len);\n\n#define payload_type_set_user_data(pt, p) (pt)->user_data = (p)\n#define payload_type_get_user_data(pt) ((pt)->user_data)\n\n/* some payload types */\n/* audio */\nORTP_VAR_PUBLIC PayloadType payload_type_pcmu8000;\nORTP_VAR_PUBLIC PayloadType payload_type_pcma8000;\nORTP_VAR_PUBLIC PayloadType payload_type_pcm8000;\nORTP_VAR_PUBLIC PayloadType payload_type_l16_mono;\nORTP_VAR_PUBLIC PayloadType payload_type_l16_stereo;\nORTP_VAR_PUBLIC PayloadType payload_type_lpc1016;\nORTP_VAR_PUBLIC PayloadType payload_type_g729;\nORTP_VAR_PUBLIC PayloadType payload_type_g7231;\nORTP_VAR_PUBLIC PayloadType payload_type_g7221;\nORTP_VAR_PUBLIC PayloadType payload_type_cn;\nORTP_VAR_PUBLIC PayloadType payload_type_g726_40;\nORTP_VAR_PUBLIC PayloadType payload_type_g726_32;\nORTP_VAR_PUBLIC PayloadType payload_type_g726_24;\nORTP_VAR_PUBLIC PayloadType payload_type_g726_16;\nORTP_VAR_PUBLIC PayloadType payload_type_aal2_g726_40;\nORTP_VAR_PUBLIC PayloadType payload_type_aal2_g726_32;\nORTP_VAR_PUBLIC PayloadType payload_type_aal2_g726_24;\nORTP_VAR_PUBLIC PayloadType payload_type_aal2_g726_16;\nORTP_VAR_PUBLIC PayloadType payload_type_gsm;\nORTP_VAR_PUBLIC PayloadType payload_type_lpc;\nORTP_VAR_PUBLIC PayloadType payload_type_lpc1015;\nORTP_VAR_PUBLIC PayloadType payload_type_speex_nb;\nORTP_VAR_PUBLIC PayloadType payload_type_speex_wb;\nORTP_VAR_PUBLIC PayloadType payload_type_speex_uwb;\nORTP_VAR_PUBLIC PayloadType payload_type_ilbc;\nORTP_VAR_PUBLIC PayloadType payload_type_amr;\nORTP_VAR_PUBLIC PayloadType payload_type_amrwb;\nORTP_VAR_PUBLIC PayloadType payload_type_truespeech;\nORTP_VAR_PUBLIC PayloadType payload_type_evrc0;\nORTP_VAR_PUBLIC PayloadType payload_type_evrcb0;\nORTP_VAR_PUBLIC PayloadType payload_type_silk_nb;\nORTP_VAR_PUBLIC PayloadType payload_type_silk_mb;\nORTP_VAR_PUBLIC PayloadType payload_type_silk_wb;\nORTP_VAR_PUBLIC PayloadType payload_type_silk_swb;\nORTP_VAR_PUBLIC PayloadType payload_type_aaceld_16k;\nORTP_VAR_PUBLIC PayloadType payload_type_aaceld_22k;\nORTP_VAR_PUBLIC PayloadType payload_type_aaceld_32k;\nORTP_VAR_PUBLIC PayloadType payload_type_aaceld_44k;\nORTP_VAR_PUBLIC PayloadType payload_type_aaceld_48k;\nORTP_VAR_PUBLIC PayloadType payload_type_opus;\nORTP_VAR_PUBLIC PayloadType payload_type_isac;\nORTP_VAR_PUBLIC PayloadType payload_type_gsm_efr;\nORTP_VAR_PUBLIC PayloadType payload_type_codec2;\nORTP_VAR_PUBLIC PayloadType payload_type_bv16;\n\n/* video */\nORTP_VAR_PUBLIC PayloadType payload_type_mpv;\nORTP_VAR_PUBLIC PayloadType payload_type_h261;\nORTP_VAR_PUBLIC PayloadType payload_type_h263;\nORTP_VAR_PUBLIC PayloadType payload_type_h263_1998;\nORTP_VAR_PUBLIC PayloadType payload_type_h263_2000;\nORTP_VAR_PUBLIC PayloadType payload_type_mp4v;\nORTP_VAR_PUBLIC PayloadType payload_type_theora;\nORTP_VAR_PUBLIC PayloadType payload_type_h264;\nORTP_VAR_PUBLIC PayloadType payload_type_h265;\nORTP_VAR_PUBLIC PayloadType payload_type_x_snow;\nORTP_VAR_PUBLIC PayloadType payload_type_jpeg;\nORTP_VAR_PUBLIC PayloadType payload_type_vp8;\nORTP_VAR_PUBLIC PayloadType payload_type_av1;\n\nORTP_VAR_PUBLIC PayloadType payload_type_g722;\nORTP_VAR_PUBLIC PayloadType payload_type_flexfec;\n/* text */\nORTP_VAR_PUBLIC PayloadType payload_type_t140;\nORTP_VAR_PUBLIC PayloadType payload_type_t140_red;\n\n/* non standard file transfer over UDP */\nORTP_VAR_PUBLIC PayloadType payload_type_x_udpftp;\n\n/* telephone-event */\nORTP_VAR_PUBLIC PayloadType payload_type_telephone_event;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/port.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n/* this file is responsible of the portability of the stack */\n\n#ifndef ORTP_PORT_H\n#define ORTP_PORT_H\n\n#include \"bctoolbox/port.h\"\n\n#ifndef ORTP_DEPRECATED\n#if defined(_MSC_VER)\n#define ORTP_DEPRECATED __declspec(deprecated)\n#else\n#define ORTP_DEPRECATED __attribute__((deprecated))\n#endif // _MSC_VER\n#endif // ORTP_DEPRECATED\n\n#if __APPLE__\n#include \"TargetConditionals.h\"\n#endif\n\ntypedef bctbx_socket_t ortp_socket_t;\ntypedef bctbx_cond_t ortp_cond_t;\ntypedef bctbx_mutex_t ortp_mutex_t;\ntypedef bctbx_thread_t ortp_thread_t;\n\n#define ortp_thread_create bctbx_thread_create\n#define ortp_thread_join bctbx_thread_join\n#define ortp_thread_self bctbx_thread_self\n#define ortp_thread_exit(arg) bctbx_thread_exit\n#define ortp_mutex_init bctbx_mutex_init\n#define ortp_mutex_lock bctbx_mutex_lock\n#define ortp_mutex_unlock bctbx_mutex_unlock\n#define ortp_mutex_destroy bctbx_mutex_destroy\n#define ortp_cond_init bctbx_cond_init\n#define ortp_cond_signal bctbx_cond_signal\n#define ortp_cond_broadcast bctbx_cond_broadcast\n#define ortp_cond_wait bctbx_cond_wait\n#define ortp_cond_destroy bctbx_cond_destroy\n\n#if !defined(_WIN32) && !defined(_WIN32_WCE)\n/********************************/\n/* definitions for UNIX flavour */\n/********************************/\n\n#include <errno.h>\n#include <fcntl.h>\n#include <pthread.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#ifdef __linux__\n#include <stdint.h>\n#endif\n\n#include <netinet/in.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#if defined(_XOPEN_SOURCE_EXTENDED) || !defined(__hpux)\n#include <arpa/inet.h>\n#endif\n\n#include <sys/time.h>\n\n#include <netdb.h>\n\n#ifdef __INTEL_COMPILER\n\n#pragma warning(disable : 111)  // statement is unreachable\n#pragma warning(disable : 181)  // argument is incompatible with corresponding format string conversion\n#pragma warning(disable : 188)  // enumerated type mixed with another type\n#pragma warning(disable : 593)  // variable \"xxx\" was set but never used\n#pragma warning(disable : 810)  // conversion from \"int\" to \"unsigned short\" may lose significant bits\n#pragma warning(disable : 869)  // parameter \"xxx\" was never referenced\n#pragma warning(disable : 981)  // operands are evaluated in unspecified order\n#pragma warning(disable : 1418) // external function definition with no prior declaration\n#pragma warning(disable : 1419) // external declaration in primary source file\n#pragma warning(disable : 1469) // \"cc\" clobber ignored\n#endif                          // __INTEL_COMPILER\n\n#define ORTP_PUBLIC\n#define ORTP_INLINE inline\n\n#define SOCKET_OPTION_VALUE void *\n#define SOCKET_BUFFER void *\n\n#define ortp_log10f(x) log10f(x)\n\n#else // !defined(_WIN32) && !defined(_WIN32_WCE)\n/*********************************/\n/* definitions for WIN32 flavour */\n/*********************************/\n\n#include <stdio.h>\n#define _CRT_RAND_S\n#include <stdarg.h>\n#include <stdlib.h>\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#ifdef _MSC_VER\n#include <io.h>\n#endif // _MSC_VER\n\n#if defined(__MINGW32__) || !defined(WINAPI_FAMILY_PARTITION) || !defined(WINAPI_PARTITION_DESKTOP)\n#define ORTP_WINDOWS_DESKTOP 1\n#elif defined(WINAPI_FAMILY_PARTITION)\n// See bctoolbox/include/port.h for WINAPI_PARTITION checker\n#if defined(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)\n#define ORTP_WINDOWS_DESKTOP 1\n#elif defined(WINAPI_PARTITION_PC_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PC_APP)\n#define ORTP_WINDOWS_DESKTOP 1\n#define ORTP_WINDOWS_UWP 1\n#elif defined(WINAPI_PARTITION_PHONE_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)\n#define ORTP_WINDOWS_PHONE 1\n#elif defined(WINAPI_PARTITION_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)\n#define ORTP_WINDOWS_UNIVERSAL 1\n#endif // defined(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)\n#endif // defined(WINAPI_FAMILY_PARTITION)\n\n#ifdef _MSC_VER\n#ifdef ORTP_STATIC\n#define ORTP_PUBLIC\n#else\n#ifdef ORTP_EXPORTS\n#define ORTP_PUBLIC __declspec(dllexport)\n#else\n#define ORTP_PUBLIC __declspec(dllimport)\n#endif\n#endif\n#pragma push_macro(\"_WINSOCKAPI_\")\n#ifndef _WINSOCKAPI_\n#define _WINSOCKAPI_\n#endif\n\ntypedef unsigned __int64 uint64_t;\ntypedef __int64 int64_t;\ntypedef unsigned short uint16_t;\ntypedef unsigned int uint32_t;\ntypedef int int32_t;\ntypedef unsigned char uint8_t;\ntypedef __int16 int16_t;\n#else // _MSC_VER\n#include <io.h>\n#include <stdint.h> /*provided by mingw32*/\n\n#endif // _MSC_VER\n\n#define SOCKET_OPTION_VALUE char *\n#define SOCKET_BUFFER void *\n#define ORTP_INLINE __inline\n\n#if defined(_WIN32_WCE)\n\n#define ortp_log10f(x) (float)log10((double)x)\n\n#ifdef assert\n#undef assert\n#endif /*assert*/\n#define assert(exp) ((void)0)\n\n#ifdef errno\n#undef errno\n#endif /*errno*/\n#define errno GetLastError()\n#ifdef strerror\n#undef strerror\n#endif /*strerror*/\nconst char *ortp_strerror(DWORD value);\n#define strerror ortp_strerror\n\n#else /*_WIN32_WCE*/\n\n#define ortp_log10f(x) log10f(x)\n\n#endif // defined(_WIN32_WCE)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifndef F_OK\n#define F_OK 00 /* Visual Studio does not define F_OK */\n#endif\n\n#ifdef _WORKAROUND_MINGW32_BUGS\nchar *WSAAPI gai_strerror(int errnum);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // _MSC_VER\n\n#ifndef _BOOL_T_\n#define _BOOL_T_\ntypedef unsigned char bool_t;\n#endif /* _BOOL_T_ */\n#undef TRUE\n#undef FALSE\n#define TRUE 1\n#define FALSE 0\n\ntypedef struct bctoolboxTimeSpec ortpTimeSpec;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ortp_malloc(sz) bctbx_malloc(sz)\n#define ortp_free(ptr) bctbx_free(ptr)\n#define ortp_realloc(ptr, sz) bctbx_realloc(ptr, sz)\n#define ortp_malloc0(sz) bctbx_malloc0(sz)\n#define ortp_strdup(str) bctbx_strdup(str)\n#define ortp_strndup(str, n) bctbx_strndup(str, n)\n\n#define ortp_new(type, count) (type *)ortp_malloc(sizeof(type) * (count))\n#define ortp_new0(type, count) (type *)ortp_malloc0(sizeof(type) * (count))\n\nORTP_PUBLIC int close_socket(ortp_socket_t sock);\nORTP_PUBLIC int set_non_blocking_socket(ortp_socket_t sock);\nORTP_PUBLIC int set_blocking_socket(ortp_socket_t sock);\n\n#define ortp_strdup_printf bctbx_strdup_printf\n#define ortp_strdup_vprintf bctbx_strdup_vprintf\n#define ortp_strcat_printf bctbx_strcat_printf\n#define ortp_strcat_vprintf bctbx_strcat_vprintf\n\n#define ortp_file_exist(pathname) bctbx_file_exist(pathname)\n\n#define ortp_get_cur_time(ts) bctbx_get_cur_time(ts)\n#define ortp_get_utc_cur_time(ts) bctbx_get_utc_cur_time(ts)\n#define ortp_get_cur_time_ms(void) bctbx_get_cur_time_ms(void)\n#define ortp_sleep_ms(ms) bctbx_sleep_ms(ms)\n#define ortp_sleep_until(ts) bctbx_sleep_until(ts)\n#define ortp_timespec_compare(t1, t2) bctbx_timespec_compare(t1, t2)\n#define ortp_random(void) bctbx_random(void)\n\n/* portable named pipes  and shared memory*/\n#if !defined(_WIN32_WCE)\n#ifdef _WIN32\ntypedef HANDLE ortp_pipe_t;\n#define ORTP_PIPE_INVALID INVALID_HANDLE_VALUE\n#else\ntypedef int ortp_pipe_t;\n#define ORTP_PIPE_INVALID (-1)\n#endif\n\n#define ortp_server_pipe_create(name) bctbx_server_pipe_create(name)\n/*\n * warning: on win32 ortp_server_pipe_accept_client() might return INVALID_HANDLE_VALUE without\n * any specific error, this happens when ortp_server_pipe_close() is called on another pipe.\n * This pipe api is not thread-safe.\n */\n#define ortp_server_pipe_accept_client(server) bctbx_server_pipe_accept_client(server)\n\n#define ortp_server_pipe_close(spipe) bctbx_server_pipe_close(spipe)\n#define ortp_server_pipe_close_client(client) bctbx_server_pipe_close_client(client)\n\n#define ortp_client_pipe_connect(name) bctbx_client_pipe_connect(name)\n#define ortp_client_pipe_close(sock) bctbx_client_pipe_close(sock)\n\n#define ortp_pipe_read(p, buf, len) bctbx_pipe_read(b, buf, len)\n#define ortp_pipe_write(p, buf, len) bctbx_pipe_write(p, buf, len)\n\n#define ortp_shm_open(keyid, size, create) bctbx_shm_open(keyid, size, create)\n#define ortp_shm_close(keyid, size, create) bctbx_shm_close(keyid, size, create)\n\n#define ortp_is_multicast_addr(addr) bctbx_is_multicast_addr(addr)\n\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(ORTP_STATIC)\n#ifdef ORTP_EXPORTS\n#define ORTP_VAR_PUBLIC extern __declspec(dllexport)\n#else\n#define ORTP_VAR_PUBLIC __declspec(dllimport)\n#endif\n#else\n#define ORTP_VAR_PUBLIC extern\n#endif\n\n#ifndef IN6_IS_ADDR_MULTICAST\n#define IN6_IS_ADDR_MULTICAST(i) (((uint8_t *)(i))[0] == 0xff)\n#endif\n\n/*define __ios when we are compiling for ios.\n The TARGET_OS_IPHONE macro is stupid, it is defined to 0 when compiling on mac os x.\n*/\n#if TARGET_OS_IPHONE\n#define __ios 1\n#endif\n\n#endif // ORTP_PORT_H\n"
  },
  {
    "path": "include/ortp/rtcp.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef RTCP_H\n#define RTCP_H\n\n#include \"ortp/str_utils.h\"\n#include <ortp/port.h>\n\n#define RTCP_MAX_RECV_BUFSIZE 1500\n\n#define RTCP_SENDER_INFO_SIZE 20\n#define RTCP_REPORT_BLOCK_SIZE 24\n#define RTCP_COMMON_HEADER_SIZE 4\n#define RTCP_SSRC_FIELD_SIZE 4\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* RTCP common header */\n\ntypedef enum {\n\tRTCP_SR = 200,\n\tRTCP_RR = 201,\n\tRTCP_SDES = 202,\n\tRTCP_BYE = 203,\n\tRTCP_APP = 204,\n\tRTCP_RTPFB = 205,\n\tRTCP_PSFB = 206,\n\tRTCP_XR = 207\n} rtcp_type_t;\n\ntypedef struct rtcp_common_header {\n#ifdef ORTP_BIGENDIAN\n\tuint16_t version : 2;\n\tuint16_t padbit : 1;\n\tuint16_t rc : 5;\n\tuint16_t packet_type : 8;\n#else\n\tuint16_t rc : 5;\n\tuint16_t padbit : 1;\n\tuint16_t version : 2;\n\tuint16_t packet_type : 8;\n#endif\n\tuint16_t length : 16;\n} rtcp_common_header_t;\n\n#define rtcp_common_header_set_version(ch, v) (ch)->version = v\n#define rtcp_common_header_set_padbit(ch, p) (ch)->padbit = p\n#define rtcp_common_header_set_rc(ch, rc) (ch)->rc = rc\n#define rtcp_common_header_set_packet_type(ch, pt) (ch)->packet_type = pt\n#define rtcp_common_header_set_length(ch, l) (ch)->length = htons(l)\n\n#define rtcp_common_header_get_version(ch) ((ch)->version)\n#define rtcp_common_header_get_padbit(ch) ((ch)->padbit)\n#define rtcp_common_header_get_rc(ch) ((ch)->rc)\n#define rtcp_common_header_get_packet_type(ch) ((ch)->packet_type)\n#define rtcp_common_header_get_length(ch) ntohs((ch)->length)\n\n/* RTCP SR or RR  packets */\n\ntypedef struct sender_info {\n\tuint32_t ntp_timestamp_msw;\n\tuint32_t ntp_timestamp_lsw;\n\tuint32_t rtp_timestamp;\n\tuint32_t senders_packet_count;\n\tuint32_t senders_octet_count;\n} sender_info_t;\n\nstatic ORTP_INLINE uint64_t sender_info_get_ntp_timestamp(const sender_info_t *si) {\n\treturn ((((uint64_t)ntohl(si->ntp_timestamp_msw)) << 32) + ((uint64_t)ntohl(si->ntp_timestamp_lsw)));\n}\n#define sender_info_get_rtp_timestamp(si) ntohl((si)->rtp_timestamp)\n#define sender_info_get_packet_count(si) ntohl((si)->senders_packet_count)\n#define sender_info_get_octet_count(si) ntohl((si)->senders_octet_count)\n\ntypedef struct report_block {\n\tuint32_t ssrc;\n\tuint32_t fl_cnpl;              /*fraction lost + cumulative number of packet lost*/\n\tuint32_t ext_high_seq_num_rec; /*extended highest sequence number received */\n\tuint32_t interarrival_jitter;\n\tuint32_t lsr;               /*last SR */\n\tuint32_t delay_snc_last_sr; /*delay since last sr*/\n} report_block_t;\n\nstatic ORTP_INLINE uint32_t report_block_get_ssrc(const report_block_t *rb) {\n\treturn ntohl(rb->ssrc);\n}\nstatic ORTP_INLINE uint32_t report_block_get_high_ext_seq(const report_block_t *rb) {\n\treturn ntohl(rb->ext_high_seq_num_rec);\n}\nstatic ORTP_INLINE uint32_t report_block_get_interarrival_jitter(const report_block_t *rb) {\n\treturn ntohl(rb->interarrival_jitter);\n}\n\nstatic ORTP_INLINE uint32_t report_block_get_last_SR_time(const report_block_t *rb) {\n\treturn ntohl(rb->lsr);\n}\nstatic ORTP_INLINE uint32_t report_block_get_last_SR_delay(const report_block_t *rb) {\n\treturn ntohl(rb->delay_snc_last_sr);\n}\nstatic ORTP_INLINE uint32_t report_block_get_fraction_lost(const report_block_t *rb) {\n\treturn (ntohl(rb->fl_cnpl) >> 24);\n}\nstatic ORTP_INLINE int32_t report_block_get_cum_packet_lost(const report_block_t *rb) {\n\tuint32_t cum_loss = (uint32_t)ntohl(rb->fl_cnpl);\n\tif (((cum_loss >> 23) & 1) == 0) return (int32_t)(0x00FFFFFF & cum_loss);\n\telse return (int32_t)(0xFF000000 | (cum_loss - 0xFFFFFF - 1));\n}\n\nstatic ORTP_INLINE void report_block_set_fraction_lost(report_block_t *rb, int fl) {\n\trb->fl_cnpl = (uint32_t)htonl(((uint32_t)ntohl(rb->fl_cnpl) & 0xFFFFFF) | ((uint32_t)fl & 0xFF) << 24);\n}\n\nstatic ORTP_INLINE void report_block_set_cum_packet_lost(report_block_t *rb, int64_t cpl) {\n\tuint32_t clamp =\n\t    (uint32_t)((1 << 24) + ((cpl >= 0) ? (cpl > 0x7FFFFF ? 0x7FFFFF : cpl) : (-cpl > 0x800000 ? -0x800000 : cpl)));\n\n\trb->fl_cnpl = htonl((ntohl(rb->fl_cnpl) & 0xFF000000) | (cpl >= 0 ? clamp & 0x7FFFFF : clamp | 0x800000));\n}\n\n/* SDES packets */\n\ntypedef enum {\n\tRTCP_SDES_END = 0,\n\tRTCP_SDES_CNAME = 1,\n\tRTCP_SDES_NAME = 2,\n\tRTCP_SDES_EMAIL = 3,\n\tRTCP_SDES_PHONE = 4,\n\tRTCP_SDES_LOC = 5,\n\tRTCP_SDES_TOOL = 6,\n\tRTCP_SDES_NOTE = 7,\n\tRTCP_SDES_PRIV = 8,\n\tRTCP_SDES_MID = 10, /* TODO: change this value when his identifier will be assigned */\n\tRTCP_SDES_MAX = 11\n} rtcp_sdes_type_t;\n\ntypedef struct sdes_chunk {\n\tuint32_t csrc;\n} sdes_chunk_t;\n\n#define sdes_chunk_get_ssrc(m) ntohl(((sdes_chunk_t *)((m)->b_rptr))->csrc)\n#define sdes_chunk_get_csrc(c) ntohl((c)->csrc)\n\ntypedef struct sdes_item {\n\tuint8_t item_type;\n\tuint8_t len;\n\tchar content[1];\n} sdes_item_t;\n\n#define RTCP_SDES_MAX_STRING_SIZE 255\n#define RTCP_SDES_ITEM_HEADER_SIZE 2\n#define RTCP_SDES_CHUNK_DEFAULT_SIZE 1024\n#define RTCP_SDES_CHUNK_HEADER_SIZE (sizeof(sdes_chunk_t))\n\n/* RTCP bye packet */\n\ntypedef struct rtcp_bye_reason {\n\tuint8_t len;\n\tchar content[1];\n} rtcp_bye_reason_t;\n\ntypedef struct rtcp_bye {\n\trtcp_common_header_t ch;\n\tuint32_t ssrc[1]; /* the bye may contain several ssrc/csrc */\n} rtcp_bye_t;\n#define RTCP_BYE_HEADER_SIZE sizeof(rtcp_bye_t)\n#define RTCP_BYE_REASON_MAX_STRING_SIZE 255\n\n/* RTCP XR packet */\n\n#define RTCP_XR_VOIP_METRICS_CONFIG_PLC_STD ((1 << 7) | (1 << 6))\n#define RTCP_XR_VOIP_METRICS_CONFIG_PLC_ENH (1 << 7)\n#define RTCP_XR_VOIP_METRICS_CONFIG_PLC_DIS (1 << 6)\n#define RTCP_XR_VOIP_METRICS_CONFIG_PLC_UNS 0\n#define RTCP_XR_VOIP_METRICS_CONFIG_JBA_ADA ((1 << 5) | (1 << 4))\n#define RTCP_XR_VOIP_METRICS_CONFIG_JBA_NON (1 << 5)\n#define RTCP_XR_VOIP_METRICS_CONFIG_JBA_UNK 0\n\ntypedef enum {\n\tRTCP_XR_LOSS_RLE = 1,\n\tRTCP_XR_DUPLICATE_RLE = 2,\n\tRTCP_XR_PACKET_RECEIPT_TIMES = 3,\n\tRTCP_XR_RCVR_RTT = 4,\n\tRTCP_XR_DLRR = 5,\n\tRTCP_XR_STAT_SUMMARY = 6,\n\tRTCP_XR_VOIP_METRICS = 7\n} rtcp_xr_block_type_t;\n\ntypedef struct rtcp_xr_header {\n\trtcp_common_header_t ch;\n\tuint32_t ssrc;\n} rtcp_xr_header_t;\n\ntypedef struct rtcp_xr_generic_block_header {\n\tuint8_t bt;\n\tuint8_t flags;\n\tuint16_t length;\n} rtcp_xr_generic_block_header_t;\n\ntypedef struct rtcp_xr_rcvr_rtt_report_block {\n\trtcp_xr_generic_block_header_t bh;\n\tuint32_t ntp_timestamp_msw;\n\tuint32_t ntp_timestamp_lsw;\n} rtcp_xr_rcvr_rtt_report_block_t;\n\ntypedef struct rtcp_xr_dlrr_report_subblock {\n\tuint32_t ssrc;\n\tuint32_t lrr;\n\tuint32_t dlrr;\n} rtcp_xr_dlrr_report_subblock_t;\n\ntypedef struct rtcp_xr_dlrr_report_block {\n\trtcp_xr_generic_block_header_t bh;\n\trtcp_xr_dlrr_report_subblock_t content[1];\n} rtcp_xr_dlrr_report_block_t;\n\ntypedef struct rtcp_xr_stat_summary_report_block {\n\trtcp_xr_generic_block_header_t bh;\n\tuint32_t ssrc;\n\tuint16_t begin_seq;\n\tuint16_t end_seq;\n\tuint32_t lost_packets;\n\tuint32_t dup_packets;\n\tuint32_t min_jitter;\n\tuint32_t max_jitter;\n\tuint32_t mean_jitter;\n\tuint32_t dev_jitter;\n\tuint8_t min_ttl_or_hl;\n\tuint8_t max_ttl_or_hl;\n\tuint8_t mean_ttl_or_hl;\n\tuint8_t dev_ttl_or_hl;\n} rtcp_xr_stat_summary_report_block_t;\n\ntypedef struct rtcp_xr_voip_metrics_report_block {\n\trtcp_xr_generic_block_header_t bh;\n\tuint32_t ssrc;\n\tuint8_t loss_rate;\n\tuint8_t discard_rate;\n\tuint8_t burst_density;\n\tuint8_t gap_density;\n\tuint16_t burst_duration;\n\tuint16_t gap_duration;\n\tuint16_t round_trip_delay;\n\tuint16_t end_system_delay;\n\tuint8_t signal_level;\n\tuint8_t noise_level;\n\tuint8_t rerl;\n\tuint8_t gmin;\n\tuint8_t r_factor;\n\tuint8_t ext_r_factor;\n\tuint8_t mos_lq;\n\tuint8_t mos_cq;\n\tuint8_t rx_config;\n\tuint8_t reserved2;\n\tuint16_t jb_nominal;\n\tuint16_t jb_maximum;\n\tuint16_t jb_abs_max;\n} rtcp_xr_voip_metrics_report_block_t;\n\n#define MIN_RTCP_XR_PACKET_SIZE (sizeof(rtcp_xr_header_t) + 4)\n\n/* RTCP FB packet */\ntypedef enum { RTCP_RTPFB_NACK = 1, RTCP_RTPFB_TMMBR = 3, RTCP_RTPFB_TMMBN = 4 } rtcp_rtpfb_type_t;\n\ntypedef enum {\n\tRTCP_PSFB_PLI = 1,\n\tRTCP_PSFB_SLI = 2,\n\tRTCP_PSFB_RPSI = 3,\n\tRTCP_PSFB_FIR = 4,\n\tRTCP_PSFB_AFB = 15\n} rtcp_psfb_type_t;\n\ntypedef struct rtcp_fb_header {\n\tuint32_t packet_sender_ssrc;\n\tuint32_t media_source_ssrc;\n} rtcp_fb_header_t;\n\ntypedef struct rtcp_fb_generic_nack_fci {\n\tuint16_t pid;\n\tuint16_t blp;\n} rtcp_fb_generic_nack_fci_t;\n\n#define rtcp_fb_generic_nack_fci_get_pid(nack) ntohs((nack)->pid)\n#define rtcp_fb_generic_nack_fci_set_pid(nack, value) ((nack)->pid) = htons(value)\n#define rtcp_fb_generic_nack_fci_get_blp(nack) ntohs((nack)->blp)\n#define rtcp_fb_generic_nack_fci_set_blp(nack, value) ((nack)->blp) = htons(value)\n\ntypedef struct rtcp_fb_tmmbr_fci {\n\tuint32_t ssrc;\n\tuint32_t value;\n} rtcp_fb_tmmbr_fci_t;\n\n#define rtcp_fb_tmmbr_fci_get_ssrc(tmmbr) ntohl((tmmbr)->ssrc)\n#define rtcp_fb_tmmbr_fci_get_mxtbr_exp(tmmbr) ((uint8_t)((ntohl((tmmbr)->value) >> 26) & 0x0000003F))\n#define rtcp_fb_tmmbr_fci_set_mxtbr_exp(tmmbr, mxtbr_exp)                                                              \\\n\t((tmmbr)->value) = htonl((ntohl((tmmbr)->value) & 0x03FFFFFF) | (((mxtbr_exp) & 0x0000003F) << 26))\n#define rtcp_fb_tmmbr_fci_get_mxtbr_mantissa(tmmbr) ((uint32_t)((ntohl((tmmbr)->value) >> 9) & 0x0001FFFF))\n#define rtcp_fb_tmmbr_fci_set_mxtbr_mantissa(tmmbr, mxtbr_mantissa)                                                    \\\n\t((tmmbr)->value) = htonl((ntohl((tmmbr)->value) & 0xFC0001FF) | (((mxtbr_mantissa) & 0x0001FFFF) << 9))\n#define rtcp_fb_tmmbr_fci_get_measured_overhead(tmmbr) ((uint16_t)(ntohl((tmmbr)->value) & 0x000001FF))\n#define rtcp_fb_tmmbr_fci_set_measured_overhead(tmmbr, measured_overhead)                                              \\\n\t((tmmbr)->value) = htonl((ntohl((tmmbr)->value) & 0xFFFFFE00) | ((measured_overhead) & 0x000001FF))\n\ntypedef struct rtcp_fb_fir_fci {\n\tuint32_t ssrc;\n\tuint8_t seq_nr;\n\tuint8_t pad1;\n\tuint16_t pad2;\n} rtcp_fb_fir_fci_t;\n\n#define rtcp_fb_fir_fci_get_ssrc(fci) ntohl((fci)->ssrc)\n#define rtcp_fb_fir_fci_get_seq_nr(fci) (fci)->seq_nr\n\ntypedef struct rtcp_fb_sli_fci {\n\tuint32_t value;\n} rtcp_fb_sli_fci_t;\n\n#define rtcp_fb_sli_fci_get_first(fci) ((uint16_t)((ntohl((fci)->value) >> 19) & 0x00001FFF))\n#define rtcp_fb_sli_fci_set_first(fci, first)                                                                          \\\n\t((fci)->value) = htonl((ntohl((fci)->value) & 0x0007FFFF) | (((first) & 0x00001FFF) << 19))\n#define rtcp_fb_sli_fci_get_number(fci) ((uint16_t)((ntohl((fci)->value) >> 6) & 0x00001FFF))\n#define rtcp_fb_sli_fci_set_number(fci, number)                                                                        \\\n\t((fci)->value) = htonl((ntohl((fci)->value) & 0xFFF8003F) | (((number) & 0x00001FFF) << 6))\n#define rtcp_fb_sli_fci_get_picture_id(fci) ((uint8_t)(ntohl((fci)->value) & 0x0000003F))\n#define rtcp_fb_sli_fci_set_picture_id(fci, picture_id)                                                                \\\n\t((fci)->value) = htonl((ntohl((fci)->value) & 0xFFFFFFC0) | ((picture_id) & 0x0000003F))\n\ntypedef struct rtcp_fb_rpsi_fci {\n\tuint8_t pb;\n\tuint8_t payload_type;\n\tuint16_t bit_string[1];\n} rtcp_fb_rpsi_fci_t;\n\n#define rtcp_fb_rpsi_fci_get_payload_type(fci) (fci)->payload_type\n#define rtcp_fb_rpsi_fci_get_bit_string(fci) ((uint8_t *)(fci)->bit_string)\n\n#define MIN_RTCP_PSFB_PACKET_SIZE (sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t))\n#define MIN_RTCP_RTPFB_PACKET_SIZE (sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t))\n\ntypedef struct rtcp_fb_goog_remb_fci {\n\tuint32_t identifier;\n\tuint32_t value;\n} rtcp_fb_goog_remb_fci_t;\n\n#define rtcp_fb_goog_remb_fci_get_num_ssrc(tmmbr) ((uint8_t)((ntohl((tmmbr)->value) >> 24) & 0x000000FF))\n#define rtcp_fb_goog_remb_fci_set_num_ssrc(tmmbr, num)                                                                 \\\n\t((tmmbr)->value) = htonl((ntohl((tmmbr)->value) & 0x00FFFFFF) | (((num) & 0x000000FF) << 24))\n#define rtcp_fb_goog_remb_fci_get_mxtbr_exp(tmmbr) ((uint8_t)((ntohl((tmmbr)->value) >> 18) & 0x0000003F))\n#define rtcp_fb_goog_remb_fci_set_mxtbr_exp(tmmbr, mxtbr_exp)                                                          \\\n\t((tmmbr)->value) = htonl((ntohl((tmmbr)->value) & 0xFF03FFFF) | (((mxtbr_exp) & 0x0000003F) << 18))\n#define rtcp_fb_goog_remb_fci_get_mxtbr_mantissa(tmmbr) ((uint32_t)(ntohl((tmmbr)->value) & 0x0003FFFF))\n#define rtcp_fb_goog_remb_fci_set_mxtbr_mantissa(tmmbr, mxtbr_mantissa)                                                \\\n\t((tmmbr)->value) = htonl((ntohl((tmmbr)->value) & 0xFFFC0000) | ((mxtbr_mantissa) & 0x0003FFFF))\n\n/* RTCP structs */\n\ntypedef struct rtcp_sr {\n\trtcp_common_header_t ch;\n\tuint32_t ssrc;\n\tsender_info_t si;\n\treport_block_t rb[1];\n} rtcp_sr_t;\n\ntypedef struct rtcp_rr {\n\trtcp_common_header_t ch;\n\tuint32_t ssrc;\n\treport_block_t rb[1];\n} rtcp_rr_t;\n\ntypedef struct rtcp_app {\n\trtcp_common_header_t ch;\n\tuint32_t ssrc;\n\tchar name[4];\n} rtcp_app_t;\n\nstruct _RtpSession;\nstruct _RtpStream;\nORTP_PUBLIC void rtp_session_rtcp_process_send(struct _RtpSession *s);\nORTP_PUBLIC void rtp_session_rtcp_process_recv(struct _RtpSession *s);\n\n#define RTCP_DEFAULT_REPORT_INTERVAL 5000 /* in milliseconds */\n\n/* packet parsing api */\n\n/*return the size of the rtcp packet*/\nORTP_PUBLIC size_t rtcp_get_size(const mblk_t *m);\n/*in case of coumpound packet, set read pointer of m to the beginning of the next RTCP\npacket */\nORTP_PUBLIC ORTP_DEPRECATED bool_t rtcp_next_packet(mblk_t *m);\n/* put the read pointer at the first RTCP packet of the compound packet (as before any previous calls ot\n * rtcp_next_packet() */\nORTP_PUBLIC ORTP_DEPRECATED void rtcp_rewind(mblk_t *m);\n\ntypedef struct _RtcpParserContext {\n\tmblk_t *current_packet;\n\tuint8_t *start;\n} RtcpParserContext;\n\nORTP_PUBLIC const mblk_t *rtcp_parser_context_init(RtcpParserContext *context, const mblk_t *compound_packet);\nORTP_PUBLIC const mblk_t *rtcp_parser_context_next_packet(RtcpParserContext *context);\nORTP_PUBLIC const mblk_t *rtcp_parser_context_start(RtcpParserContext *context);\nORTP_PUBLIC void rtcp_parser_context_uninit(RtcpParserContext *context);\n\n/* get common header*/\nORTP_PUBLIC const rtcp_common_header_t *rtcp_get_common_header(const mblk_t *m);\n\n/*Sender Report accessors */\n/* check if this packet is a SR and if it is correct */\nORTP_PUBLIC bool_t rtcp_is_SR(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_SR_get_ssrc(const mblk_t *m);\nORTP_PUBLIC const sender_info_t *rtcp_SR_get_sender_info(const mblk_t *m);\nORTP_PUBLIC const report_block_t *rtcp_SR_get_report_block(const mblk_t *m, int idx);\n\n/*Receiver report accessors*/\nORTP_PUBLIC bool_t rtcp_is_RR(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_RR_get_ssrc(const mblk_t *m);\nORTP_PUBLIC const report_block_t *rtcp_RR_get_report_block(const mblk_t *m, int idx);\n\n/*SDES accessors */\nORTP_PUBLIC bool_t rtcp_is_SDES(const mblk_t *m);\ntypedef void (*SdesItemFoundCallback)(\n    void *user_data, uint32_t csrc, rtcp_sdes_type_t t, const char *content, uint8_t content_len);\nORTP_PUBLIC void rtcp_sdes_parse(const mblk_t *m, SdesItemFoundCallback cb, void *user_data);\n\n/*BYE accessors */\nORTP_PUBLIC bool_t rtcp_is_BYE(const mblk_t *m);\nORTP_PUBLIC bool_t rtcp_BYE_get_ssrc(const mblk_t *m, int idx, uint32_t *ssrc);\nORTP_PUBLIC bool_t rtcp_BYE_get_reason(const mblk_t *m, const char **reason, int *reason_len);\n\n/*APP accessors */\nORTP_PUBLIC bool_t rtcp_is_APP(const mblk_t *m);\nORTP_PUBLIC int rtcp_APP_get_subtype(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_APP_get_ssrc(const mblk_t *m);\n/* name argument is supposed to be at least 4 characters (note: no '\\0' written)*/\nORTP_PUBLIC void rtcp_APP_get_name(const mblk_t *m, char *name);\n/* retrieve the data. when returning, data points directly into the mblk_t */\nORTP_PUBLIC void rtcp_APP_get_data(const mblk_t *m, uint8_t **data, int *len);\n\n/* RTCP XR accessors */\nORTP_PUBLIC bool_t rtcp_is_XR(const mblk_t *m);\nORTP_PUBLIC rtcp_xr_block_type_t rtcp_XR_get_block_type(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_get_ssrc(const mblk_t *m);\nORTP_PUBLIC uint64_t rtcp_XR_rcvr_rtt_get_ntp_timestamp(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_dlrr_get_ssrc(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_dlrr_get_lrr(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_dlrr_get_dlrr(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_stat_summary_get_flags(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_stat_summary_get_ssrc(const mblk_t *m);\nORTP_PUBLIC uint16_t rtcp_XR_stat_summary_get_begin_seq(const mblk_t *m);\nORTP_PUBLIC uint16_t rtcp_XR_stat_summary_get_end_seq(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_stat_summary_get_lost_packets(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_stat_summary_get_dup_packets(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_stat_summary_get_min_jitter(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_stat_summary_get_max_jitter(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_stat_summary_get_mean_jitter(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_stat_summary_get_dev_jitter(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_stat_summary_get_min_ttl_or_hl(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_stat_summary_get_max_ttl_or_hl(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_stat_summary_get_mean_ttl_or_hl(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_stat_summary_get_dev_ttl_or_hl(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_XR_voip_metrics_get_ssrc(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_loss_rate(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_discard_rate(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_burst_density(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_gap_density(const mblk_t *m);\nORTP_PUBLIC uint16_t rtcp_XR_voip_metrics_get_burst_duration(const mblk_t *m);\nORTP_PUBLIC uint16_t rtcp_XR_voip_metrics_get_gap_duration(const mblk_t *m);\nORTP_PUBLIC uint16_t rtcp_XR_voip_metrics_get_round_trip_delay(const mblk_t *m);\nORTP_PUBLIC uint16_t rtcp_XR_voip_metrics_get_end_system_delay(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_signal_level(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_noise_level(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_rerl(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_gmin(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_r_factor(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_ext_r_factor(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_mos_lq(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_mos_cq(const mblk_t *m);\nORTP_PUBLIC uint8_t rtcp_XR_voip_metrics_get_rx_config(const mblk_t *m);\nORTP_PUBLIC uint16_t rtcp_XR_voip_metrics_get_jb_nominal(const mblk_t *m);\nORTP_PUBLIC uint16_t rtcp_XR_voip_metrics_get_jb_maximum(const mblk_t *m);\nORTP_PUBLIC uint16_t rtcp_XR_voip_metrics_get_jb_abs_max(const mblk_t *m);\n\n/* RTCP RTPFB accessors */\nORTP_PUBLIC bool_t rtcp_is_RTPFB(const mblk_t *m);\nORTP_PUBLIC rtcp_rtpfb_type_t rtcp_RTPFB_get_type(const mblk_t *m);\nORTP_PUBLIC rtcp_fb_generic_nack_fci_t *rtcp_RTPFB_generic_nack_get_fci(const mblk_t *m);\nORTP_PUBLIC rtcp_fb_tmmbr_fci_t *rtcp_RTPFB_tmmbr_get_fci(const mblk_t *m);\nORTP_PUBLIC rtcp_fb_goog_remb_fci_t *rtcp_PSFB_goog_remb_get_fci(const mblk_t *m);\n/**\n * Return the maximum bitrate in bits / sec contained in the packet.\n *\n * @param m RTCP TMMBR packet to read\n * @return maximum bitrate in bits / sec.\n */\nORTP_PUBLIC uint64_t rtcp_RTPFB_tmmbr_get_max_bitrate(const mblk_t *m);\nORTP_PUBLIC uint64_t rtcp_PSFB_goog_remb_get_max_bitrate(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_RTPFB_get_packet_sender_ssrc(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_RTPFB_get_media_source_ssrc(const mblk_t *m);\n\n/* RTCP PSFB accessors */\nORTP_PUBLIC bool_t rtcp_is_PSFB(const mblk_t *m);\nORTP_PUBLIC rtcp_psfb_type_t rtcp_PSFB_get_type(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_PSFB_get_packet_sender_ssrc(const mblk_t *m);\nORTP_PUBLIC uint32_t rtcp_PSFB_get_media_source_ssrc(const mblk_t *m);\nORTP_PUBLIC rtcp_fb_fir_fci_t *rtcp_PSFB_fir_get_fci(const mblk_t *m, unsigned int idx);\nORTP_PUBLIC rtcp_fb_sli_fci_t *rtcp_PSFB_sli_get_fci(const mblk_t *m, unsigned int idx);\nORTP_PUBLIC rtcp_fb_rpsi_fci_t *rtcp_PSFB_rpsi_get_fci(const mblk_t *m);\nORTP_PUBLIC uint16_t rtcp_PSFB_rpsi_get_fci_bit_string_len(const mblk_t *m);\nORTP_PUBLIC bool_t rtcp_PSFB_is_goog_remb(const mblk_t *m);\n\ntypedef struct OrtpLossRateEstimator {\n\tint min_packet_count_interval;\n\tuint64_t min_time_ms_interval;\n\tuint64_t last_estimate_time_ms;\n\tint32_t last_cum_loss;\n\tint32_t last_ext_seq;\n\tfloat loss_rate;\n\t/**\n\t * Total number of outgoing duplicate packets on last\n\t * ortp_loss_rate_estimator_process_report_block iteration.\n\t **/\n\tint64_t last_dup_packet_sent_count;\n\t/**\n\t * Total number of outgoing unique packets on last\n\t * ortp_loss_rate_estimator_process_report_block iteration.\n\t **/\n\tint64_t last_packet_sent_count;\n} OrtpLossRateEstimator;\n\nORTP_PUBLIC OrtpLossRateEstimator *\nortp_loss_rate_estimator_new(int min_packet_count_interval, uint64_t min_time_ms_interval, struct _RtpSession *session);\n\nORTP_PUBLIC void ortp_loss_rate_estimator_init(OrtpLossRateEstimator *obj,\n                                               int min_packet_count_interval,\n                                               uint64_t min_time_ms_interval,\n                                               struct _RtpSession *session);\n\n/**\n * Process an incoming report block to compute loss rate percentage. It tries to compute\n * loss rate, depending on the previous report block. It may fails if the two\n * reports are too close or if a discontinuity occurred. You should NOT use\n * loss rate field of the report block directly (see below).\n * This estimator is useful for two reasons: first, on AVPF session, multiple\n * reports can be received in a short period and loss_rate contained in these\n * reports is unreliable. Secondly, it computes the loss rate using the\n * cumulative loss factor which allows us to take into consideration duplicates\n * packets as well.\n * @param[in] obj #OrtpLossRateEstimator object.\n * @param[in] session #_RtpSession stream in which the report block to consider belongs.\n * @param[in] rb Report block to analyze.\n * @return TRUE if a new loss rate estimation is ready, FALSE otherwise.\n */\nORTP_PUBLIC bool_t ortp_loss_rate_estimator_process_report_block(OrtpLossRateEstimator *obj,\n                                                                 const struct _RtpSession *session,\n                                                                 const report_block_t *rb);\n/**\n * Get the latest loss rate in percentage estimation computed.\n *\n * @param obj #OrtpLossRateEstimator object.\n * @return The latest loss rate in percentage computed.\n */\nORTP_PUBLIC float ortp_loss_rate_estimator_get_value(OrtpLossRateEstimator *obj);\n\nORTP_PUBLIC void ortp_loss_rate_estimator_destroy(OrtpLossRateEstimator *obj);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/rtp.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef RTP_H\n#define RTP_H\n\n#include <ortp/port.h>\n#include <ortp/str_utils.h>\n\n#define IPMAXLEN 20\n#define UDP_MAX_SIZE 1500\n#define RTP_FIXED_HEADER_SIZE 12\n#define RTP_DEFAULT_JITTER_TIME 80             /*miliseconds*/\n#define RTP_DEFAULT_MULTICAST_TTL 5            /*hops*/\n#define RTP_DEFAULT_MULTICAST_LOOPBACK 0       /*false*/\n#define RTP_DEFAULT_DSCP 0x00                  /*best effort*/\n#define RTP_MAX_MIXER_TO_CLIENT_AUDIO_LEVEL 15 /* 15 because we can only put 15 csrc in a rtp packet */\n\ntypedef struct rtp_header {\n#ifdef ORTP_BIGENDIAN\n\tuint16_t version : 2;\n\tuint16_t padbit : 1;\n\tuint16_t extbit : 1;\n\tuint16_t cc : 4;\n\tuint16_t markbit : 1;\n\tuint16_t paytype : 7;\n#else\n\tuint16_t cc : 4;\n\tuint16_t extbit : 1;\n\tuint16_t padbit : 1;\n\tuint16_t version : 2;\n\tuint16_t paytype : 7;\n\tuint16_t markbit : 1;\n#endif\n\tuint16_t seq_number;\n\tuint32_t timestamp;\n\tuint32_t ssrc;\n\tuint32_t csrc[16];\n} rtp_header_t;\n\ntypedef struct rtp_stats {\n\tuint64_t packet_sent;       /*number of outgoing packets */\n\tuint64_t packet_dup_sent;   /*number of outgoing duplicate packets */\n\tuint64_t sent;              /* outgoing total bytes (excluding IP header) */\n\tuint64_t packet_recv;       /* number of incoming packets */\n\tuint64_t packet_dup_recv;   /* number of incoming duplicate packets */\n\tuint64_t recv;              /* incoming bytes of payload and delivered in time to the application */\n\tuint64_t hw_recv;           /* incoming bytes of payload */\n\tuint64_t outoftime;         /* number of incoming packets that were received too late */\n\tint64_t cum_packet_loss;    /* cumulative number of incoming packet lost */\n\tuint64_t bad;               /* incoming packets that did not appear to be RTP */\n\tuint64_t discarded;         /* incoming packets discarded because the queue exceeds its max size */\n\tuint64_t sent_rtcp_packets; /* outgoing RTCP packets counter */\n\tuint64_t recv_rtcp_packets; /* incoming RTCP packets counter */\n\tuint64_t loss_before_nack;  /*Number of packets asked for a resend*/\n} rtp_stats_t;\n\ntypedef struct jitter_stats {\n\tuint32_t jitter;             /* interarrival jitter at last emitted sender report */\n\tuint32_t max_jitter;         /* biggest interarrival jitter (value in stream clock unit) */\n\tuint64_t sum_jitter;         /* sum of all interarrival jitter (value in stream clock unit) */\n\tuint64_t max_jitter_ts;      /* date (in ms since Epoch) of the biggest interarrival jitter */\n\tfloat jitter_buffer_size_ms; /* mean jitter buffer size in milliseconds.*/\n} jitter_stats_t;\n\n/* MAX is 15 because we use 1-byte header */\ntypedef enum {\n\tRTP_EXTENSION_NONE = 0,\n\tRTP_EXTENSION_MID = 1,\n\tRTP_EXTENSION_CLIENT_TO_MIXER_AUDIO_LEVEL = 2,\n\tRTP_EXTENSION_MIXER_TO_CLIENT_AUDIO_LEVEL = 3,\n\tRTP_EXTENSION_FRAME_MARKING = 4,\n\tRTP_EXTENSION_MAX = 15\n} rtp_extension_type_t;\n\ntypedef struct rtp_audio_level {\n\tuint32_t csrc;\n\tint dbov;\n} rtp_audio_level_t;\n\n#define RTP_AUDIO_LEVEL_NO_VOLUME -255\n\n#define RTP_FRAME_MARKER_START (1 << 7)\n#define RTP_FRAME_MARKER_END (1 << 6)\n#define RTP_FRAME_MARKER_INDEPENDENT (1 << 5)\n#define RTP_FRAME_MARKER_DISCARDABLE (1 << 4)\n\n#define RTP_TIMESTAMP_IS_NEWER_THAN(ts1, ts2) ((uint32_t)((uint32_t)(ts1) - (uint32_t)(ts2)) < ((uint32_t)1 << 31))\n\n#define RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(ts1, ts2)                                                                 \\\n\t(((uint32_t)((uint32_t)(ts1) - (uint32_t)(ts2)) < ((uint32_t)1 << 31)) && (ts1) != (ts2))\n\n#define RTP_SEQ_IS_STRICTLY_GREATER_THAN(seq1, seq2)                                                                   \\\n\t(((uint16_t)((uint16_t)(seq1) - (uint16_t)(seq2)) < ((uint16_t)1 << 15)) && (seq1) != (seq2))\n\n#define TIME_IS_NEWER_THAN(t1, t2) RTP_TIMESTAMP_IS_NEWER_THAN(t1, t2)\n\n#define TIME_IS_STRICTLY_NEWER_THAN(t1, t2) RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(t1, t2)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* packet api */\n/* the first argument is a rtp_header_t  */\n#define rtp_header_set_seqnumber(hdr, seq) (hdr)->seq_number = (htons(seq))\n#define rtp_header_set_timestamp(hdr, ts) (hdr)->timestamp = (htonl(ts))\n#define rtp_header_set_ssrc(hdr, _ssrc) (hdr)->ssrc = (htonl(_ssrc))\nORTP_PUBLIC void rtp_header_add_csrc(rtp_header_t *hdr, uint32_t csrc);\n\n#define rtp_header_get_seqnumber(hdr) (ntohs((hdr)->seq_number))\n#define rtp_header_get_timestamp(hdr) (ntohl((hdr)->timestamp))\n#define rtp_header_get_ssrc(hdr) (ntohl((hdr)->ssrc))\n#define rtp_header_get_csrc(hdr, idx) (ntohl((hdr)->csrc[idx]))\n\n/* the first argument is a mblk_t. The header is supposed to be not splitted  */\n#define rtp_set_version(mp, value) ((rtp_header_t *)((mp)->b_rptr))->version = (value)\n#define rtp_set_padbit(mp, value) ((rtp_header_t *)((mp)->b_rptr))->padbit = (value)\n#define rtp_set_extbit(mp, value) ((rtp_header_t *)((mp)->b_rptr))->extbit = (value)\n#define rtp_set_cc(mp, value) ((rtp_header_t *)((mp)->b_rptr))->cc = (value)\n#define rtp_set_markbit(mp, value) ((rtp_header_t *)((mp)->b_rptr))->markbit = (value)\n#define rtp_set_payload_type(mp, pt) ((rtp_header_t *)((mp)->b_rptr))->paytype = (pt)\n#define rtp_set_seqnumber(mp, seq) rtp_header_set_seqnumber((rtp_header_t *)((mp)->b_rptr), (seq))\n#define rtp_set_timestamp(mp, ts) rtp_header_set_timestamp((rtp_header_t *)((mp)->b_rptr), (ts))\n#define rtp_set_ssrc(mp, _ssrc) rtp_header_set_ssrc((rtp_header_t *)((mp)->b_rptr), (_ssrc))\nORTP_PUBLIC void rtp_add_csrc(mblk_t *mp, uint32_t csrc);\n\n#define rtp_get_version(mp) (((rtp_header_t *)((mp)->b_rptr))->version)\n#define rtp_get_padbit(mp) (((rtp_header_t *)((mp)->b_rptr))->padbit)\n#define rtp_get_markbit(mp) (((rtp_header_t *)((mp)->b_rptr))->markbit)\n#define rtp_get_extbit(mp) (((rtp_header_t *)((mp)->b_rptr))->extbit)\n#define rtp_get_timestamp(mp) rtp_header_get_timestamp((rtp_header_t *)((mp)->b_rptr))\n#define rtp_get_seqnumber(mp) rtp_header_get_seqnumber((rtp_header_t *)((mp)->b_rptr))\n#define rtp_get_payload_type(mp) (((rtp_header_t *)((mp)->b_rptr))->paytype)\n#define rtp_get_ssrc(mp) rtp_header_get_ssrc((rtp_header_t *)((mp)->b_rptr))\n#define rtp_get_cc(mp) (((rtp_header_t *)((mp)->b_rptr))->cc)\n#define rtp_get_csrc(mp, idx) rtp_header_get_csrc((rtp_header_t *)((mp)->b_rptr), (idx))\n\nORTP_PUBLIC int rtp_get_payload(mblk_t *packet, unsigned char **start);\nORTP_PUBLIC int rtp_get_extheader(const mblk_t *packet, uint16_t *profile, uint8_t **start_ext);\n\n/* Extension header api */\nORTP_PUBLIC void rtp_add_extension_header(mblk_t *packet, int id, size_t size, uint8_t *data);\nORTP_PUBLIC void rtp_write_extension_header(mblk_t *packet, int id, size_t size, uint8_t *data);\nORTP_PUBLIC void rtp_delete_extension_header(mblk_t *packet, int id);\nORTP_PUBLIC int rtp_get_extension_header(const mblk_t *packet, int id, uint8_t **data);\nORTP_PUBLIC void rtp_remap_header_extension_ids(mblk_t *packet, const int mapping[16]);\n\n/* Audio Level api */\nORTP_PUBLIC void rtp_add_client_to_mixer_audio_level(mblk_t *packet, int id, bool_t voice_activity, int audio_level);\nORTP_PUBLIC int rtp_get_client_to_mixer_audio_level(mblk_t *packet, int id, bool_t *voice_activity);\n\nORTP_PUBLIC void\nrtp_add_mixer_to_client_audio_level(mblk_t *packet, int id, size_t size, const rtp_audio_level_t *audio_levels);\nORTP_PUBLIC void\nrtp_write_mixer_to_client_audio_level(mblk_t *packet, int id, size_t size, const rtp_audio_level_t *audio_levels);\nORTP_PUBLIC int rtp_get_mixer_to_client_audio_level(mblk_t *packet, int id, rtp_audio_level_t *audio_levels);\n\n/* Frame marking api */\nORTP_PUBLIC void rtp_add_frame_marker(mblk_t *packet, int id, uint8_t marker);\nORTP_PUBLIC int rtp_get_frame_marker(const mblk_t *packet, int id, uint8_t *marker);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/rtpprofile.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/**\n * \\file rtpprofile.h\n * \\brief Using and creating standart and custom RTP profiles\n *\n **/\n\n#ifndef RTPPROFILE_H\n#define RTPPROFILE_H\n#include <ortp/port.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define RTP_PROFILE_MAX_PAYLOADS 128\n\n/**\n * The RTP profile is a table RTP_PROFILE_MAX_PAYLOADS entries to make the matching\n * between RTP payload type number and the PayloadType that defines the type of\n * media.\n **/\nstruct _RtpProfile {\n\tchar *name;\n\tPayloadType *payload[RTP_PROFILE_MAX_PAYLOADS];\n};\n\ntypedef struct _RtpProfile RtpProfile;\n\nORTP_VAR_PUBLIC RtpProfile av_profile;\n\n#define rtp_profile_get_name(profile) (const char *)((profile)->name)\n\nORTP_PUBLIC void rtp_profile_set_payload(RtpProfile *prof, int idx, PayloadType *pt);\n\n/**\n *\tSet payload type number \\a index unassigned in the profile.\n *\n *@param profile an RTP profile\n *@param index\tthe payload type number\n **/\n#define rtp_profile_clear_payload(profile, index) rtp_profile_set_payload(profile, index, NULL)\n\n/* I prefer have this function inlined because it is very often called in the code */\n/**\n *\n *\tGets the payload description of the payload type \\a index in the profile.\n *\n *@param prof an RTP profile (a #_RtpProfile object)\n *@param idx\tthe payload type number\n *@return the payload description (a PayloadType object)\n **/\nstatic ORTP_INLINE PayloadType *rtp_profile_get_payload(const RtpProfile *prof, int idx) {\n\tif (idx < 0 || idx >= RTP_PROFILE_MAX_PAYLOADS) {\n\t\treturn NULL;\n\t}\n\treturn prof->payload[idx];\n}\nORTP_PUBLIC void rtp_profile_clear_all(RtpProfile *prof);\nORTP_PUBLIC void rtp_profile_set_name(RtpProfile *prof, const char *name);\nORTP_PUBLIC PayloadType *rtp_profile_get_payload_from_mime(RtpProfile *profile, const char *mime);\nORTP_PUBLIC PayloadType *rtp_profile_get_payload_from_rtpmap(RtpProfile *profile, const char *rtpmap);\nORTP_PUBLIC int rtp_profile_get_payload_number_from_mime(RtpProfile *profile, const char *mime);\nORTP_PUBLIC int rtp_profile_get_payload_number_from_mime_and_flag(RtpProfile *profile, const char *mime, int flag);\nORTP_PUBLIC int rtp_profile_get_payload_number_from_rtpmap(RtpProfile *profile, const char *rtpmap);\nORTP_PUBLIC int rtp_profile_find_payload_number(RtpProfile *prof, const char *mime, int rate, int channels);\nORTP_PUBLIC PayloadType *rtp_profile_find_payload(RtpProfile *prof, const char *mime, int rate, int channels);\nORTP_PUBLIC int rtp_profile_move_payload(RtpProfile *prof, int oldpos, int newpos);\n\nORTP_PUBLIC RtpProfile *rtp_profile_new(const char *name);\n/* clone a profile, payload are not cloned */\nORTP_PUBLIC RtpProfile *rtp_profile_clone(RtpProfile *prof);\n\n/*clone a profile and its payloads (ie payload type are newly allocated, not reusing payload types of the reference\n * profile) */\nORTP_PUBLIC RtpProfile *rtp_profile_clone_full(RtpProfile *prof);\n/* frees the profile and all its PayloadTypes*/\nORTP_PUBLIC void rtp_profile_destroy(RtpProfile *prof);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/rtpsession.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/**\n * \\file rtpsession.h\n * \\brief The RtpSession api\n *\n * The RtpSession objects represent a RTP session: once it is configured with\n * local and remote network addresses and a payload type is given, it let you send\n * and recv a media stream.\n **/\n\n#ifndef RTPSESSION_H\n#define RTPSESSION_H\n\n#include <bctoolbox/list.h>\n\n#include <ortp/event.h>\n#include <ortp/payloadtype.h>\n#include <ortp/port.h>\n#include <ortp/rtcp.h>\n#include <ortp/rtp.h>\n#include <ortp/rtpprofile.h>\n#include <ortp/rtpsignaltable.h>\n#include <ortp/sessionset.h>\n#include <ortp/str_utils.h>\n#include <ortp/utils.h>\n\n#define ORTP_AVPF_FEATURE_NONE 0\n#define ORTP_AVPF_FEATURE_TMMBR (1 << 0)\n#define ORTP_AVPF_FEATURE_GENERIC_NACK (1 << 1)\n#define ORTP_AVPF_FEATURE_IMMEDIATE_NACK (1 << 2)\n#define ORTP_AVPF_FEATURE_GOOG_REMB (1 << 3)\n\ntypedef enum { RTP_SESSION_RECVONLY, RTP_SESSION_SENDONLY, RTP_SESSION_SENDRECV } RtpSessionMode;\n\ntypedef enum _OrtpJitterBufferAlgorithm {\n\tOrtpJitterBufferBasic,\n\tOrtpJitterBufferRecursiveLeastSquare,\n} OrtpJitterBufferAlgorithm;\n\n/*! Jitter buffer parameters\n */\ntypedef struct _JBParameters {\n\tint min_size;    /*(adaptive=TRUE only) maximum dynamic delay to be added to incoming packets (ms) */\n\tint nom_size;    /*(adaptive=TRUE only) initial dynamic delay to be added to incoming packets (ms) */\n\tint max_size;    /*(adaptive=TRUE only) minimum dynamic delay to be added to incoming packets (ms) */\n\tbool_t adaptive; /*either a dynamic buffer should be used or not to compensate bursts */\n\tbool_t enabled;  /*whether jitter buffer is enabled*/\n\tbool_t pad[2];   /*(dev only) alignment pad: insert your bool_t here*/\n\tint max_packets; /**max number of packets allowed to be queued in the jitter buffer */\n\tOrtpJitterBufferAlgorithm buffer_algorithm;\n\tint refresh_ms;      /* (adaptive=TRUE only) dynamic buffer size update frequency (ms) */\n\tint ramp_threshold;  /*(adaptive=TRUE, algo=RLS only) Percentage in [0;100] threshold between current jitter and\n\t                        previous jitter to enable smooth ramp*/\n\tint ramp_step_ms;    /*(adaptive=TRUE, algo=RLS only) In smooth ramp, how much we should reduce jitter size on each\n\t                        step*/\n\tint ramp_refresh_ms; /*(adaptive=TRUE, algo=RLS only) In smooth ramp, frequency of step*/\n} JBParameters;\n\ntypedef struct _JitterControl {\n\tJBParameters params;\n\tunsigned int count; /* number of packets handled in jitter_control_new_packet. Used internally only. */\n\tint jitt_comp_ts;   /* the nominal jitter buffer size converted in rtp time (same unit as timestamp) */\n\tint adapt_jitt_comp_ts;\n\tint32_t clock_offset_ts; /*offset difference between local and distant clock, in timestamp units*/\n\tint32_t prev_clock_offset_ts;\n\tint32_t olddiff;\n\tfloat jitter;\n\tfloat inter_jitter;            /* interarrival jitter as defined in the RFC */\n\tfloat jitter_buffer_mean_size; /*effective size (fullness) of jitter buffer*/\n\tint corrective_step;\n\tint corrective_slide;\n\tuint64_t cum_jitter_buffer_size;      /*in timestamp units*/\n\tunsigned int cum_jitter_buffer_count; /*used for computation of jitter buffer size*/\n\tint clock_rate;\n\tuint32_t adapt_refresh_prev_ts; /*last time we refreshed the buffer*/\n\tOrtpExtremum max_ts_deviation;  /*maximum difference between packet and expected timestamps */\n\tOrtpKalmanRLS kalman_rls;\n\tdouble capped_clock_ratio;\n\tuint32_t last_log_ts;\n\tuint32_t local_ts_start;\n\tuint32_t remote_ts_start;\n\tuint32_t diverged_start_ts;\n\tbool_t is_diverging;\n\tbool_t jb_size_updated;\n\tbool_t pad[2];\n} JitterControl;\n\ntypedef struct _WaitPoint {\n\tortp_mutex_t lock;\n\tortp_cond_t cond;\n\tuint32_t time;\n\tbool_t wakeup;\n} WaitPoint;\n\ntypedef enum {\n\tRtpTransportModifierLevelEncryption,\n\tRtpTransportModifierLevelForwardErrorCorrection,\n\tRtpTransportModifierLevelAudioBandwidthEstimator, // The audio bandwith estimator must be executed last(in sending)\n\t                                                  // and first (in receiving), keep this enum last\n} RtpTransportModifierLevel;\n\n#define ORTP_RTP_TRANSPORT_MODIFIER_DEFAULT_LEVEL RtpTransportModifierLevelEncryption\n\ntypedef struct _RtpTransportModifier {\n\tvoid *data;\n\tRtpTransportModifierLevel level;\n\tstruct _RtpSession *session;     //<back pointer to the owning session, set by oRTP\n\tstruct _RtpTransport *transport; //<back point to the owning transport, set by oRTP\n\tint (*t_process_on_send)(struct _RtpTransportModifier *t, mblk_t *msg);\n\tint (*t_process_on_receive)(struct _RtpTransportModifier *t, mblk_t *msg);\n\tvoid (*t_process_on_schedule)(struct _RtpTransportModifier *t); /*invoked each time rtp_session_recvm is called even\n\t                                                                   is no message are available*/\n\t/**\n\t * Mandatory callback responsible of freeing the #_RtpTransportModifier AND the pointer.\n\t * @param[in] transport #_RtpTransportModifier object to free.\n\t */\n\tvoid (*t_destroy)(struct _RtpTransportModifier *transport);\n} RtpTransportModifier;\n\ntypedef struct _RtpTransport {\n\tvoid *data;\n\tstruct _RtpSession *session; //<back pointer to the owning session, set by oRTP\n\tortp_socket_t (*t_getsocket)(struct _RtpTransport *t);\n\tint (*t_sendto)(struct _RtpTransport *t, mblk_t *msg, int flags, const struct sockaddr *to, socklen_t tolen);\n\tint (*t_recvfrom)(struct _RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen);\n\tvoid (*t_close)(struct _RtpTransport *transport);\n\t/**\n\t * Mandatory callback responsible of freeing the #_RtpTransport object AND the pointer.\n\t * @param[in] transport #_RtpTransport object to free.\n\t */\n\tvoid (*t_destroy)(struct _RtpTransport *transport);\n} RtpTransport;\n\ntypedef enum _OrtpNetworkSimulatorMode {\n\tOrtpNetworkSimulatorInvalid = -1,\n\tOrtpNetworkSimulatorInbound,           /**<simulation is applied when receiving packets*/\n\tOrtpNetworkSimulatorOutbound,          /**<simulation is applied to sent packets*/\n\tOrtpNetworkSimulatorOutboundControlled /**<simulation is applied to sent packets according to sent timestamps\n\t            set in the timestamps field of mblk_t, which is defined only with -DORTP_TIMESTAMP */\n} OrtpNetworkSimulatorMode;\n\n/**\n * Structure describing the network simulator parameters\n **/\ntypedef struct _OrtpNetworkSimulatorParams {\n\tint enabled;                        /**<Whether simulation is enabled or off.*/\n\tfloat max_bandwidth;                /**<IP bandwidth, in bit/s.\n\t                                   This limitation is applied after loss are simulated, so incoming bandwidth\n\t                                   is NOT socket bandwidth, but after-loss-simulation bandwidth e.g with 50% loss, the bandwidth\n\t                                   will be 50% reduced*/\n\tint max_buffer_size;                /**<Max number of bit buffered before being discarded*/\n\tfloat loss_rate;                    /**<Percentage of lost packets*/\n\tuint32_t latency;                   /**<Packet transmission delay, in ms*/\n\tfloat consecutive_loss_probability; /**< a probability of having a subsequent loss after a loss occurred, in a 0-1\n\t                                       range. Useful to simulate burst of lost packets*/\n\tfloat jitter_burst_density;         /**<density of gap/bursts events. A value of 1 means one gap/burst per second\n\t                                       approximately*/\n\tfloat jitter_strength;              /**<percentage of max_bandwidth artificially consumed during bursts events*/\n\tbool_t rtp_only;                    /**True for only RTP packet loss, False for both RTP and RTCP */\n\tbool_t pad[3];\n\tOrtpNetworkSimulatorMode mode; /**<whether simulation is applied to inbound or outbound stream.*/\n} OrtpNetworkSimulatorParams;\n\ntypedef struct _OrtpNetworkSimulatorCtx {\n\tOrtpNetworkSimulatorParams params;\n\tint bit_budget;\n\tint qsize;\n\tqueue_t q; /*queue used for simulating bandwidth limit*/\n\tqueue_t latency_q;\n\tqueue_t send_q; /*used only for OrtpNetworkSimulatorOutbound direction*/\n\tstruct timeval last_check;\n\tuint64_t last_jitter_event;\n\tint consecutive_drops;\n\tint drops_to_ignore;\n\tint drop_by_congestion;\n\tint drop_by_loss;\n\tint total_count; /*total number of packets gone through the simulator*/\n\tortp_thread_t thread;\n\tbool_t in_jitter_event;\n\tbool_t thread_started;\n} OrtpNetworkSimulatorCtx;\n\ntypedef struct OrtpRtcpSendAlgorithm {\n\tuint64_t tn;             /* Time of the next scheduled RTCP RR transmission in milliseconds. */\n\tuint64_t tp;             /* Time of the last scheduled RTCP RR transmission in milliseconds. */\n\tuint64_t t_rr_last;      /* Time of the last regular RTCP packet sent in milliseconds. */\n\tuint32_t T_rr;           /* Interval for the scheduling of the next regular RTCP packet. */\n\tuint32_t T_max_fb_delay; /* Interval within which a feeback message is considered to be useful to the sender. */\n\tuint32_t T_rr_interval;  /* Minimal interval to be used between regular RTCP packets. */\n\tuint32_t T_rr_current_interval;\n\tuint32_t Tmin; /* Minimal interval between RTCP packets. */\n\tfloat avg_rtcp_size;\n\tmblk_t *fb_packets;\n\tbool_t initialized; /* Whether the RTCP send algorithm is fully initialized. */\n\tbool_t initial;\n\tbool_t allow_early;\n\tbool_t tmmbr_scheduled;\n\tbool_t tmmbn_scheduled;\n\tbool_t goog_remb_scheduled;\n} OrtpRtcpSendAlgorithm;\n\ntypedef struct OrtpRtcpFbConfiguration {\n\tbool_t generic_nack_enabled;\n\tbool_t tmmbr_enabled;\n\tbool_t goog_remb_enabled;\n} OrtpRtcpFbConfiguration;\n\n#define ORTP_RTCP_XR_UNAVAILABLE_PARAMETER 127\n\ntypedef enum { OrtpRtcpXrNoPlc, OrtpRtcpXrSilencePlc, OrtpRtcpXrEnhancedPlc } OrtpRtcpXrPlcStatus;\n\ntypedef OrtpRtcpXrPlcStatus (*OrtpRtcpXrPlcCallback)(void *userdata);\ntypedef int (*OrtpRtcpXrSignalLevelCallback)(void *userdata);\ntypedef int (*OrtpRtcpXrNoiseLevelCallback)(void *userdata);\ntypedef float (*OrtpRtcpXrAverageQualityIndicatorCallback)(void *userdata);\n\ntypedef struct OrtpRtcpXrMediaCallbacks {\n\tOrtpRtcpXrPlcCallback plc;\n\tOrtpRtcpXrSignalLevelCallback signal_level;\n\tOrtpRtcpXrNoiseLevelCallback noise_level;\n\tOrtpRtcpXrAverageQualityIndicatorCallback average_qi;\n\tOrtpRtcpXrAverageQualityIndicatorCallback average_lq_qi;\n\tvoid *userdata;\n} OrtpRtcpXrMediaCallbacks;\n\ntypedef enum { OrtpRtcpXrRcvrRttNone, OrtpRtcpXrRcvrRttAll, OrtpRtcpXrRcvrRttSender } OrtpRtcpXrRcvrRttMode;\n\ntypedef enum {\n\tOrtpRtcpXrStatSummaryNone = 0,\n\tOrtpRtcpXrStatSummaryLoss = (1 << 7),\n\tOrtpRtcpXrStatSummaryDup = (1 << 6),\n\tOrtpRtcpXrStatSummaryJitt = (1 << 5),\n\tOrtpRtcpXrStatSummaryTTL = (1 << 3),\n\tOrtpRtcpXrStatSummaryHL = (1 << 4)\n} OrtpRtcpXrStatSummaryFlag;\n\ntypedef struct OrtpRtcpXrConfiguration {\n\tbool_t enabled;\n\tbool_t stat_summary_enabled;\n\tbool_t voip_metrics_enabled;\n\tbool_t pad;\n\tOrtpRtcpXrRcvrRttMode rcvr_rtt_mode;\n\tint rcvr_rtt_max_size;\n\tOrtpRtcpXrStatSummaryFlag stat_summary_flags;\n} OrtpRtcpXrConfiguration;\n\ntypedef struct OrtpRtcpXrStats {\n\tuint32_t last_rcvr_rtt_ts;             /* NTP timestamp (middle 32 bits) of last received XR rcvr-rtt */\n\tstruct timeval last_rcvr_rtt_time;     /* Time at which last XR rcvr-rtt was received  */\n\tuint16_t rcv_seq_at_last_stat_summary; /* Received sequence number at last XR stat-summary sent */\n\tuint32_t rcv_since_last_stat_summary;  /* The number of packets received since last XR stat-summary was sent */\n\tuint32_t\n\t    dup_since_last_stat_summary; /* The number of duplicate packets received since last XR stat-summary was sent */\n\tuint32_t min_jitter_since_last_stat_summary; /* The minimum value of jitter since last XR stat-summary was sent */\n\tuint32_t max_jitter_since_last_stat_summary; /* The maximum value of jitter since last XR stat-summary was sent */\n\tdouble olds_jitter_since_last_stat_summary;\n\tdouble oldm_jitter_since_last_stat_summary;\n\tdouble news_jitter_since_last_stat_summary;\n\tdouble newm_jitter_since_last_stat_summary;\n\tint64_t last_jitter_diff_since_last_stat_summary;\n\tdouble olds_ttl_or_hl_since_last_stat_summary;\n\tdouble oldm_ttl_or_hl_since_last_stat_summary;\n\tdouble news_ttl_or_hl_since_last_stat_summary;\n\tdouble newm_ttl_or_hl_since_last_stat_summary;\n\tuint8_t min_ttl_or_hl_since_last_stat_summary; /* The minimum value of TTL/HL since last XR stat-summary was sent */\n\tuint8_t max_ttl_or_hl_since_last_stat_summary; /* The maximum value of TTL/HL since last XR stat-summary was sent */\n\tuint32_t first_rcv_seq;\n\tuint32_t last_rcv_seq;\n\tuint32_t rcv_count;\n\tuint32_t discarded_count;\n} OrtpRtcpXrStats;\n\ntypedef struct OrtpRtcpTmmbrInfo {\n\tmblk_t *sent;\n\tmblk_t *received;\n} OrtpRtcpTmmbrInfo;\n\ntypedef struct OrtpRtcpGooRembInfo {\n\tmblk_t *sent;\n\tuint64_t sent_time;\n} OrtpRtcpGooRembInfo;\n\ntypedef struct _OrtpAddress {\n\tstruct sockaddr_storage addr;\n\tsocklen_t len;\n} OrtpAddress;\n\ntypedef struct _OrtpStream {\n\tortp_socket_t socket;\n\tint sockfamily;\n\tint loc_port;\n\tsocklen_t rem_addrlen;\n\tstruct sockaddr_storage rem_addr;\n\tsocklen_t rem_addr_previously_set_len;\n\tstruct sockaddr_storage rem_addr_previously_set;\n\tsocklen_t loc_addrlen;\n\tstruct sockaddr_storage loc_addr;\n\tsocklen_t used_loc_addrlen;\n\tstruct sockaddr_storage used_loc_addr; /*Address used to redirect packets from this source*/\n\tstruct _RtpTransport *tr;\n\tOrtpBandwidthMeasurer *recv_bw_estimator;\n\tOrtpBandwidthMeasurer *recv_average_bw_estimator;\n\tOrtpBandwidthMeasurer *send_bw_estimator;\n\tOrtpBandwidthMeasurer *send_average_bw_estimator;\n\tbctbx_list_t *aux_destinations; /*list of OrtpAddress */\n\tqueue_t bundleq;                /* For bundle mode */\n\tortp_mutex_t bundleq_lock;\n\tbool_t remote_address_adaptation;\n} OrtpStream;\n\ntypedef struct _RtpStream {\n\tOrtpStream gs;\n\tint time_jump;\n\tuint32_t ts_jump;\n\tqueue_t rq;\n\tqueue_t tev_rq;\n\tvoid *QoSHandle;\n\tunsigned long QoSFlowID;\n\tJitterControl jittctl;\n\tuint32_t snd_time_offset;     /*the scheduler time when the application send its first timestamp*/\n\tuint32_t snd_ts_offset;       /* the first application timestamp sent by the application */\n\tuint32_t snd_rand_offset;     /* a random number added to the user offset to make the stream timestamp*/\n\tuint32_t snd_last_ts;         /* the last stream timestamp sent */\n\tuint16_t snd_last_nack;       /* the last nack sent when in immediate mode */\n\tuint32_t rcv_time_offset;     /*the scheduler time when the application ask for its first timestamp*/\n\tuint32_t rcv_ts_offset;       /* the first stream timestamp */\n\tuint32_t rcv_query_ts_offset; /* the first user timestamp asked by the application */\n\tuint32_t rcv_last_ts;         /* the last stream timestamp got by the application */\n\tuint16_t rcv_last_seq;        /* the last stream sequence number got by the application*/\n\tuint16_t snd_seq;             /* send sequence number */\n\tuint32_t rcv_last_app_ts;     /* the last application timestamp asked by the application */\n\tuint32_t rcv_last_ret_ts;     /* the timestamp of the last sample returned (only for continuous audio)*/\n\tuint32_t hwrcv_extseq;        /* last received on socket extended sequence number */\n\tuint32_t hwrcv_seq_at_last_SR;\n\tuint32_t hwrcv_since_last_SR;\n\tuint32_t last_rcv_SR_ts;         /* NTP timestamp (middle 32 bits) of last received SR */\n\tstruct timeval last_rcv_SR_time; /* time at which last SR was received  */\n\tuint32_t last_rtcp_packet_count; /*the sender's octet count in the last sent RTCP SR*/\n\tuint32_t sent_payload_bytes;     /*used for RTCP sender reports*/\n\tint recv_errno;\n\tint send_errno;\n\tint snd_socket_size;\n\tint rcv_socket_size;\n\tint ssrc_changed_thres;\n\tjitter_stats_t jitter_stats;\n\tstruct _OrtpCongestionDetector *congdetect;\n\tstruct _OrtpVideoBandwidthEstimator *video_bw_estimator;\n\tstruct _OrtpAudioBandwidthEstimator *audio_bw_estimator;\n\tortp_thread_t win_t;\n\tvolatile bool_t is_win_thread_running;\n\tortp_mutex_t winthread_lock;\n\tqueue_t winrq;\n\tortp_mutex_t winrq_lock;\n} RtpStream;\n\ntypedef struct _RtcpStream {\n\tOrtpStream gs;\n\tOrtpRtcpSendAlgorithm send_algo;\n\tOrtpRtcpXrConfiguration xr_conf;\n\tOrtpRtcpXrMediaCallbacks xr_media_callbacks;\n\tOrtpRtcpTmmbrInfo tmmbr_info;\n\tOrtpRtcpGooRembInfo goog_remb_info;\n\tbool_t enabled; /*tells whether we can send RTCP packets */\n\tbool_t rtcp_xr_dlrr_to_send;\n\tuint8_t rtcp_fb_fir_seq_nr; /* The FIR command sequence number */\n\tuint32_t last_rtcp_fb_pli_snt;\n} RtcpStream;\n\ntypedef struct _RtcpSdesItems {\n\tchar *cname;\n\tchar *name;\n\tchar *email;\n\tchar *phone;\n\tchar *loc;\n\tchar *tool;\n\tchar *note;\n} RtcpSdesItems;\n\ntypedef struct _RtpSession RtpSession;\n\n/**\n * An object representing a bi-directional RTP session.\n * It holds sockets, jitter buffer, various counters (timestamp, sequence numbers...)\n * Applications SHOULD NOT try to read things within the RtpSession object but use\n * instead its public API (the rtp_session_* methods) where RtpSession is used as a\n * pointer.\n * rtp_session_new() allocates and initialize a RtpSession.\n **/\nstruct _RtpSession {\n\tortp_mutex_t main_mutex; /* To protect data that can be accessed simultaneously by a control thread and the\n\t                            real-time thread in charge of sending/receiving. */\n\tRtpSession *next;        /* next RtpSession, when the session are enqueued by the scheduler */\n\tint mask_pos; /* the position in the scheduler mask of RtpSession : do not move this field: it is part of the ABI\n\t                 since the session_set macros use it*/\n\tstruct {\n\t\tRtpProfile *profile;\n\t\tint pt;\n\t\tunsigned int ssrc;\n\t\tWaitPoint wp;\n\t} snd, rcv;\n\tunsigned int inc_ssrc_candidate;\n\tint inc_same_ssrc_count;\n\tint hw_recv_pt; /* recv payload type before jitter buffer */\n\tint recv_buf_size;\n\tint target_upload_bandwidth;     /* Target upload bandwidth at network layer (with IP and UDP headers) in bits/s */\n\tint max_target_upload_bandwidth; /* the largest target upload bandwidth at network layer (with IP and UDP headers)\n\t                                    in bits/s ever set through rtp_session_set_target_upload_bandwidth */\n\tRtpSignalTable on_ssrc_changed;\n\tRtpSignalTable on_payload_type_changed;\n\tRtpSignalTable on_telephone_event_packet;\n\tRtpSignalTable on_telephone_event;\n\tRtpSignalTable on_timestamp_jump;\n\tRtpSignalTable on_network_error;\n\tRtpSignalTable on_rtcp_bye;\n\tRtpSignalTable on_new_incoming_ssrc_in_bundle; /**< triggered when we cannot find a session with this send.ssrc when\n\t                                                  looking for it while dispatching an incoming packet in a bundle\n\t                                                  and no free sessions are found */\n\tRtpSignalTable\n\t    on_new_outgoing_ssrc_in_bundle; /**< triggered when we cannot find a session with this send.ssrc when looking\n\t                                       for it while doing rtp_bundle_lookup_session_for_outgoing_packet */\n\tbctbx_list_t *signal_tables;\n\tbctbx_list_t *eventqs;\n\tRtpStream rtp;\n\tRtcpStream rtcp;\n\tOrtpRtcpXrStats rtcp_xr_stats;\n\tRtpSessionMode mode;\n\tstruct _RtpScheduler *sched;\n\tmblk_t *recv_block_cache;\n\tuint32_t flags;\n\tint dscp;\n\tint multicast_ttl;\n\tint multicast_loopback;\n\tfloat duplication_ratio; /* Number of times a packet should be duplicated */\n\tfloat duplication_left;  /* Remainder of the duplication ratio, internal use */\n\tvoid *user_data;\n\t/* FIXME: Should be a table for all session participants. */\n\tstruct timeval last_recv_time; /* Time of receiving the RTP/RTCP packet. */\n\tmblk_t *pending;\n\t/* telephony events extension */\n\tint tev_send_pt;     /*telephone event to be used for sending*/\n\tmblk_t *current_tev; /* the pending telephony events */\n\tqueue_t contributing_sources;\n\tint lost_packets_test_vector;\n\tunsigned int interarrival_jitter_test_vector;\n\tunsigned int delay_test_vector;\n\tfloat rtt; /*last round trip delay calculated*/\n\tint cum_loss;\n\tOrtpNetworkSimulatorCtx *net_sim_ctx;\n\tRtpSession *spliced_session; /*a RtpSession that will retransmit everything received on this session*/\n\trtp_stats_t stats;\n\tbctbx_list_t *recv_addr_map;\n\tuint32_t send_ts_offset; /*additional offset to add when sending packets */\n\t/* bundle mode */\n\tstruct _RtpBundle *bundle; /* back pointer to the rtp bundle object */\n\tint mid_sent;\n\tuint64_t last_mid_sent_time;\n\t/* fec option */\n\tstruct _FecStream *fec_stream;\n\tRtcpSdesItems sdes_items;\n\tbool_t symmetric_rtp;\n\tbool_t permissive;  /*use the permissive algorithm*/\n\tbool_t use_connect; /* use connect() on the socket */\n\tbool_t ssrc_set;\n\n\tbool_t reuseaddr; /*setsockopt SO_REUSEADDR */\n\tbool_t rtcp_mux;\n\tunsigned char avpf_features; /**< A bitmask of ORTP_AVPF_FEATURE_* macros. */\n\tbool_t use_pktinfo;\n\n\tbool_t is_spliced;\n\tbool_t congestion_detector_enabled;\n\tbool_t video_bandwidth_estimator_enabled;\n\tbool_t is_primary; /* tells if this session is the primary of the rtp bundle */\n\n\tbool_t warn_non_working_pkt_info;\n\tbool_t transfer_mode;\n\tbool_t audio_bandwidth_estimator_enabled;\n};\n\n/**\n * Structure describing the video bandwidth estimator parameters\n **/\ntypedef struct _OrtpVideoBandwidthEstimatorParams {\n\tint enabled;                        /**<Whether estimator is enabled or off.*/\n\tunsigned int packet_count_min;      /**<minimum number of packets with the same sent timestamp to be processed\n\t                                       continuously before being used */\n\tunsigned min_required_measurements; /**<Minimum number of measurements required to make an estimate */\n\tunsigned int\n\t    trust_percentage; /**<percentage for which the chosen bandwidth value in all available will be inferior */\n} OrtpVideoBandwidthEstimatorParams;\n\n/**\n * Structure describing the audio bandwidth estimator parameters\n **/\ntypedef struct _OrtpAudioBandwidthEstimatorParams {\n\tint enabled;                       /**<Whether estimator is enabled or off.*/\n\tunsigned int packets_history_size; /**< number of packets needed to compute the available video bandwidth */\n\tunsigned int\n\t    trust_percentage; /**< percentage for which the chosen bandwidth value in all available will be inferior */\n\tunsigned int duplicated_packet_rate; /**< the rate packets are duplicated by sender */\n} OrtpAudioBandwidthEstimatorParams;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nORTP_PUBLIC const char *ortp_network_simulator_mode_to_string(OrtpNetworkSimulatorMode mode);\nORTP_PUBLIC OrtpNetworkSimulatorMode ortp_network_simulator_mode_from_string(const char *str);\n\n/* public API */\nORTP_PUBLIC RtpSession *rtp_session_new(RtpSessionMode mode);\nORTP_PUBLIC void rtp_session_set_mode(RtpSession *session, RtpSessionMode mode);\nORTP_PUBLIC void rtp_session_set_scheduling_mode(RtpSession *session, int yesno);\nORTP_PUBLIC void rtp_session_set_blocking_mode(RtpSession *session, int yesno);\nORTP_PUBLIC void rtp_session_set_profile(RtpSession *session, RtpProfile *profile);\nORTP_PUBLIC void rtp_session_set_send_profile(RtpSession *session, RtpProfile *profile);\nORTP_PUBLIC void rtp_session_set_recv_profile(RtpSession *session, RtpProfile *profile);\nORTP_PUBLIC RtpProfile *rtp_session_get_profile(RtpSession *session);\nORTP_PUBLIC RtpProfile *rtp_session_get_send_profile(RtpSession *session);\nORTP_PUBLIC RtpProfile *rtp_session_get_recv_profile(RtpSession *session);\nORTP_PUBLIC int\nrtp_session_signal_connect(RtpSession *session, const char *signal_name, RtpCallback cb, void *user_data);\nORTP_PUBLIC int rtp_session_signal_connect_from_source_session(\n    RtpSession *session, const char *signal_name, RtpCallback cb, void *user_data, const RtpSession *source);\nORTP_PUBLIC int rtp_session_signal_disconnect_by_callback(RtpSession *session, const char *signal_name, RtpCallback cb);\nORTP_PUBLIC int rtp_session_signal_disconnect_by_callback_and_user_data(RtpSession *session,\n                                                                        const char *signal_name,\n                                                                        RtpCallback cb,\n                                                                        void *user_data);\nORTP_PUBLIC int\nrtp_session_signal_disconnect_by_source_session(RtpSession *session, const char *signal_name, const RtpSession *source);\nORTP_PUBLIC void rtp_session_set_ssrc(RtpSession *session, uint32_t ssrc);\nORTP_PUBLIC uint32_t rtp_session_get_send_ssrc(const RtpSession *session);\nORTP_PUBLIC uint32_t rtp_session_get_recv_ssrc(RtpSession *session);\nORTP_PUBLIC void rtp_session_set_seq_number(RtpSession *session, uint16_t seq);\nORTP_PUBLIC uint16_t rtp_session_get_seq_number(RtpSession *session);\nORTP_PUBLIC uint32_t rtp_session_get_rcv_ext_seq_number(RtpSession *session);\nORTP_PUBLIC int rtp_session_get_cum_loss(RtpSession *session);\nORTP_PUBLIC void rtp_session_set_duplication_ratio(RtpSession *session, float ratio);\n\nORTP_PUBLIC void rtp_session_enable_jitter_buffer(RtpSession *session, bool_t enabled);\nORTP_PUBLIC bool_t rtp_session_jitter_buffer_enabled(const RtpSession *session);\nORTP_PUBLIC void rtp_session_set_jitter_buffer_params(RtpSession *session, const JBParameters *par);\nORTP_PUBLIC void rtp_session_get_jitter_buffer_params(RtpSession *session, JBParameters *par);\n\n/**\n * Set an additional timestamps offset for outgoing stream..\n * @param s\t\ta rtp session freshly created.\n * @param offset\t\ta timestamp offset value\n *\n **/\nORTP_PUBLIC void rtp_session_set_send_ts_offset(RtpSession *s, uint32_t offset);\nORTP_PUBLIC uint32_t rtp_session_get_send_ts_offset(RtpSession *s);\n\n/*deprecated jitter control functions*/\nORTP_PUBLIC void rtp_session_set_jitter_compensation(RtpSession *session, int milisec);\nORTP_PUBLIC void rtp_session_enable_adaptive_jitter_compensation(RtpSession *session, bool_t val);\nORTP_PUBLIC bool_t rtp_session_adaptive_jitter_compensation_enabled(RtpSession *session);\n\nORTP_PUBLIC void rtp_session_set_time_jump_limit(RtpSession *session, int miliseconds);\n/*\n * Join a multicast group.\n * @deprecated Prefer using rtp_session_set_local_addr() by specifying multicast address and port to listen to.\n */\nORTP_PUBLIC int rtp_session_join_multicast_group(RtpSession *session, const char *ip);\nORTP_PUBLIC int rtp_session_set_local_addr(RtpSession *session, const char *addr, int rtp_port, int rtcp_port);\nORTP_PUBLIC int rtp_session_get_local_port(const RtpSession *session);\nORTP_PUBLIC int rtp_session_get_local_rtcp_port(const RtpSession *session);\n\nORTP_PUBLIC int rtp_session_set_remote_addr_full(\n    RtpSession *session, const char *rtp_addr, int rtp_port, const char *rtcp_addr, int rtcp_port);\n/*same as previous function, old name:*/\nORTP_PUBLIC int\nrtp_session_set_remote_addr_and_port(RtpSession *session, const char *addr, int rtp_port, int rtcp_port);\nORTP_PUBLIC int rtp_session_set_remote_addr(RtpSession *session, const char *addr, int port);\nORTP_PUBLIC int rtp_session_add_aux_remote_addr_full(\n    RtpSession *session, const char *rtp_addr, int rtp_port, const char *rtcp_addr, int rtcp_port);\nORTP_PUBLIC void rtp_session_clear_aux_remote_addr(RtpSession *session);\n/* alternatively to the set_remote_addr() and set_local_addr(), an application can give\na valid socket (potentially connect()ed )to be used by the RtpSession */\nORTP_PUBLIC void rtp_session_set_sockets(RtpSession *session, int rtpfd, int rtcpfd);\n\nORTP_PUBLIC void rtp_session_get_transports(const RtpSession *session, RtpTransport **rtptr, RtpTransport **rtcptr);\n/*those methods are provided for people who wants to send non-RTP messages using the RTP/RTCP sockets */\nORTP_PUBLIC ortp_socket_t rtp_session_get_rtp_socket(const RtpSession *session);\nORTP_PUBLIC ortp_socket_t rtp_session_get_rtcp_socket(const RtpSession *session);\nORTP_PUBLIC void rtp_session_refresh_sockets(RtpSession *session);\n\n/* QOS / DSCP */\nORTP_PUBLIC int rtp_session_set_dscp(RtpSession *session, int dscp);\nORTP_PUBLIC int rtp_session_get_dscp(const RtpSession *session);\n\n/* Packet info */\nORTP_PUBLIC int rtp_session_set_pktinfo(RtpSession *session, int activate);\n\n/* Multicast methods */\nORTP_PUBLIC int rtp_session_set_multicast_ttl(RtpSession *session, int ttl);\nORTP_PUBLIC int rtp_session_get_multicast_ttl(RtpSession *session);\n\nORTP_PUBLIC int rtp_session_set_multicast_loopback(RtpSession *session, int yesno);\nORTP_PUBLIC int rtp_session_get_multicast_loopback(RtpSession *session);\n\nORTP_PUBLIC int rtp_session_set_send_payload_type(RtpSession *session, int paytype);\nORTP_PUBLIC int rtp_session_get_send_payload_type(const RtpSession *session);\n\nORTP_PUBLIC int rtp_session_get_recv_payload_type(const RtpSession *session);\nORTP_PUBLIC int rtp_session_set_recv_payload_type(RtpSession *session, int pt);\n\nORTP_PUBLIC int rtp_session_set_send_telephone_event_payload_type(RtpSession *session, int paytype);\n\nORTP_PUBLIC int rtp_session_set_payload_type(RtpSession *session, int pt);\n\nORTP_PUBLIC void rtp_session_set_symmetric_rtp(RtpSession *session, bool_t yesno);\n\nORTP_PUBLIC bool_t rtp_session_get_symmetric_rtp(const RtpSession *session);\n\nORTP_PUBLIC void rtp_session_enable_rtcp_mux(RtpSession *session, bool_t yesno);\n\nORTP_PUBLIC bool_t rtp_session_rtcp_mux_enabled(RtpSession *session);\n\nORTP_PUBLIC void rtp_session_set_connected_mode(RtpSession *session, bool_t yesno);\n\nORTP_PUBLIC void rtp_session_enable_rtcp(RtpSession *session, bool_t yesno);\n/*\n * rtcp status\n * @return TRUE if rtcp is enabled for this session\n */\nORTP_PUBLIC bool_t rtp_session_rtcp_enabled(const RtpSession *session);\n\nORTP_PUBLIC void rtp_session_set_rtcp_report_interval(RtpSession *session, int value_ms);\n\n/**\n * Define the bandwidth available for RTCP streams based on the upload bandwidth\n * targeted by the application (in bits/s). RTCP streams would not take more than\n * a few percents of the limit bandwidth (around 5%).\n *\n * @param session a rtp session\n * @param target_bandwidth bandwidth limit in bits/s\n */\nORTP_PUBLIC void rtp_session_set_target_upload_bandwidth(RtpSession *session, int target_bandwidth);\nORTP_PUBLIC int rtp_session_get_target_upload_bandwidth(RtpSession *session);\n\nORTP_PUBLIC void rtp_session_configure_rtcp_xr(RtpSession *session, const OrtpRtcpXrConfiguration *config);\nORTP_PUBLIC void rtp_session_set_rtcp_xr_media_callbacks(RtpSession *session, const OrtpRtcpXrMediaCallbacks *cbs);\n\nORTP_PUBLIC void rtp_session_set_ssrc_changed_threshold(RtpSession *session, int numpackets);\n\n/* low level packet creation function */\n/* deprecated set : use create_packet_header and then chain a payload mblk_t to it */\nORTP_PUBLIC ORTP_DEPRECATED mblk_t *\nrtp_session_create_packet(RtpSession *session, size_t header_size, const uint8_t *payload, size_t payload_size);\nORTP_PUBLIC ORTP_DEPRECATED mblk_t *\nrtp_session_create_packet_with_data(RtpSession *session, uint8_t *payload, size_t payload_size, void (*freefn)(void *));\nORTP_PUBLIC ORTP_DEPRECATED mblk_t *\nrtp_session_create_packet_with_mixer_to_client_audio_level(RtpSession *session,\n                                                           size_t header_size,\n                                                           int mtc_extension_id,\n                                                           size_t audio_levels_size,\n                                                           rtp_audio_level_t *audio_levels,\n                                                           const uint8_t *payload,\n                                                           size_t payload_size);\nORTP_PUBLIC ORTP_DEPRECATED mblk_t *rtp_session_create_packet_raw(const uint8_t *packet, size_t packet_size);\n/* end of deprecated functions set */\n\n/**\n * Allocates a new rtp packet. In the header, ssrc and payload_type according to the session's\n * context. Timestamp is not set, it will be set when the packet is going to be\n * sent with rtp_session_sendm_with_ts(). Sequence number is initalized to previous sequence number sent + 1\n *\n * @param[in] \tsession \t\ta rtp session.\n * @param[in] \textra_header_size \theader size is computed according to needs(CSRC, extension header).\n *\t\t\t\t\tAllocate extra size (when caller knows it will add other extensions or payload) to avoid\n *reallocating buffers\n *\n * @return a rtp packet in a mblk_t (message block) structure holding a packet header.\n **/\nORTP_PUBLIC mblk_t *rtp_session_create_packet_header(RtpSession *session, size_t extra_header_size);\n\n/**\n * Allocates a new rtp packet. In the header, ssrc and payload_type according to the session's\n * context. Add a CSRC fetched from source session SSRC.\n * Timestamp is not set, it will be set when the packet is going to be\n * sent with rtp_session_sendm_with_ts(). Sequence number is initalized to previous sequence number sent + 1\n *\n * @param[in] \tfecSession \t\tThe RTP session used to build the header (bundle and SSRC fetched from this one)\n * @param[in] \tsourceSession \t\tThe SSRC from this RTP session is set as CSRC in the header\n * @param[in] \textra_header_size \theader size is computed according to needs(CSRC, extension header).\n *\t\t\t\t\tAllocate extra size (when caller knows it will add other extensions or payload) to avoid\n *reallocating buffers\n *\n * @return a rtp packet in a mblk_t (message block) structure holding a packet header.\n **/\nORTP_PUBLIC mblk_t *\nrtp_session_create_repair_packet_header(RtpSession *fecSession, RtpSession *sourceSession, size_t extra_header_size);\n\n/**\n *\tThis will do the same as rtp_session_create_packet_header() but it will also add\n *\tmixer to client audio level indication through header extensions.\n *\n * @param[in]\tsession\t\t\ta rtp session.\n * @param[in]\textra_header_size \textra size allocated to the underlying mblk_t, use it to avoid reallocation cause by\n *future extension or payload added\n * @param[in]\tmtc_extension_id \tid of the mixer to client extension id.\n * @param[in]\taudio_levels_size\tsize of audio levels contained in audio_levels parameter.\n * @param[in]\taudio_levels\t\tlist of rtp_audio_level_t to add in this packet.\n *\n * @return a rtp packet in a mblk_t (message block) structure.\n **/\nORTP_PUBLIC mblk_t *rtp_session_create_packet_header_with_mixer_to_client_audio_level(RtpSession *session,\n                                                                                      size_t extra_header_size,\n                                                                                      int mtc_extension_id,\n                                                                                      size_t audio_levels_size,\n                                                                                      rtp_audio_level_t *audio_levels);\n\n/** create a packet from the given buffer. No header is added, the buffer is copied in a mblk_t allocated for this\n * purpose use to create non RTP packets (ZRTP, DTLS, STUN) or set a payload in a message (for CNG for example)\n * @param[in] packet\t\tpointer to the data to be copied in the created packet\n * @param[in] packet_size\tsize of data buffer\n *\n * @return a packet in a message block structure holding the given buffer\n */\nORTP_PUBLIC mblk_t *rtp_create_packet(const uint8_t *packet, size_t packet_size);\n\n/** create a packet from the given buffer. No header is added, the buffer is not copied but integrated to the packet\n * @param[in] packet\t\tpointer to the data to be copied in the created packet\n * @param[in] packet_size\tsize of data buffer\n * @param[in] freefn\t\ta function that will be called when the payload buffer is no more needed.\n *\n * @return a packet in a message block structure holding the given buffer\n */\nORTP_PUBLIC mblk_t *rtp_package_packet(uint8_t *packet, size_t packet_size, void (*freefn)(void *));\n\n/*low level recv and send functions */\n\nORTP_PUBLIC mblk_t *rtp_session_recvm_with_ts(RtpSession *session, uint32_t user_ts);\nORTP_PUBLIC int rtp_session_sendm_with_ts(RtpSession *session, mblk_t *mp, uint32_t userts);\nORTP_PUBLIC int rtp_session_sendto(\n    RtpSession *session, bool_t is_rtp, mblk_t *m, int flags, const struct sockaddr *destaddr, socklen_t destlen);\nORTP_PUBLIC int rtp_session_recvfrom(\n    RtpSession *session, bool_t is_rtp, mblk_t *m, int flags, struct sockaddr *from, socklen_t *fromlen);\n/* high level recv and send functions */\nORTP_PUBLIC int rtp_session_recv_with_ts(RtpSession *session, uint8_t *buffer, int len, uint32_t ts, int *have_more);\nORTP_PUBLIC int rtp_session_send_with_ts(RtpSession *session, const uint8_t *buffer, int len, uint32_t userts);\n\n/* Specific function called to reset the winrq queue and if called on windows to stop the async reception thread */\nORTP_PUBLIC void rtp_session_reset_recvfrom(RtpSession *session);\n\n/* event API*/\nORTP_PUBLIC void rtp_session_register_event_queue(RtpSession *session, OrtpEvQueue *q);\nORTP_PUBLIC void rtp_session_unregister_event_queue(RtpSession *session, OrtpEvQueue *q);\nORTP_PUBLIC void rtp_session_unregister_event_queues(RtpSession *session);\n\n/* IP bandwidth usage estimation functions, returning bits/s*/\nORTP_PUBLIC float rtp_session_get_send_bandwidth(RtpSession *session);\nORTP_PUBLIC float rtp_session_get_recv_bandwidth(RtpSession *session);\nORTP_PUBLIC float rtp_session_get_rtp_send_bandwidth(RtpSession *session);\nORTP_PUBLIC float rtp_session_get_rtp_recv_bandwidth(RtpSession *session);\nORTP_PUBLIC float rtp_session_get_rtcp_send_bandwidth(RtpSession *session);\nORTP_PUBLIC float rtp_session_get_rtcp_recv_bandwidth(RtpSession *session);\n\nORTP_PUBLIC float rtp_session_get_send_bandwidth_smooth(RtpSession *session);\nORTP_PUBLIC float rtp_session_get_recv_bandwidth_smooth(RtpSession *session);\n\nORTP_PUBLIC void\nrtp_session_send_rtcp_APP(RtpSession *session, uint8_t subtype, const char *name, const uint8_t *data, int datalen);\n/**\n *\tSend the rtcp datagram \\a packet to the destination set by rtp_session_set_remote_addr()\n *  The packet (\\a packet) is freed once it is sent.\n *\n * @param session a rtp session.\n * @param m a rtcp packet presented as a mblk_t.\n * @return the number of bytes sent over the network.\n **/\n\nORTP_PUBLIC int rtp_session_rtcp_sendm_raw(RtpSession *session, mblk_t *m);\n\nORTP_PUBLIC uint32_t rtp_session_get_current_send_ts(RtpSession *session);\nORTP_PUBLIC uint32_t rtp_session_get_current_recv_ts(RtpSession *session);\nORTP_PUBLIC void rtp_session_flush_sockets(RtpSession *session);\nORTP_PUBLIC void rtp_session_release_sockets(RtpSession *session);\nORTP_PUBLIC void rtp_session_resync(RtpSession *session);\nORTP_PUBLIC void rtp_session_reset(RtpSession *session);\nORTP_PUBLIC void rtp_session_destroy(RtpSession *session);\n\nORTP_PUBLIC const rtp_stats_t *rtp_session_get_stats(const RtpSession *session);\nORTP_PUBLIC const jitter_stats_t *rtp_session_get_jitter_stats(const RtpSession *session);\nORTP_PUBLIC void rtp_session_reset_stats(RtpSession *session);\n\nORTP_PUBLIC void rtp_session_set_data(RtpSession *session, void *data);\nORTP_PUBLIC void *rtp_session_get_data(const RtpSession *session);\n\nORTP_PUBLIC void rtp_session_set_recv_buf_size(RtpSession *session, int bufsize);\nORTP_PUBLIC void rtp_session_set_rtp_socket_send_buffer_size(RtpSession *session, unsigned int size);\nORTP_PUBLIC void rtp_session_set_rtp_socket_recv_buffer_size(RtpSession *session, unsigned int size);\n\n/* in use with the scheduler to convert a timestamp in scheduler time unit (ms) */\nORTP_PUBLIC uint32_t rtp_session_ts_to_time(RtpSession *session, uint32_t timestamp);\nORTP_PUBLIC uint32_t rtp_session_time_to_ts(RtpSession *session, int millisecs);\n/* this function aims at simulating senders with \"imprecise\" clocks, resulting in\nrtp packets sent with timestamp uncorrelated with the system clock .\nThis is only availlable to sessions working with the oRTP scheduler */\nORTP_PUBLIC void rtp_session_make_time_distorsion(RtpSession *session, int milisec);\n\n/*RTCP functions */\nORTP_PUBLIC void rtp_session_set_source_description(RtpSession *session,\n                                                    const char *cname,\n                                                    const char *name,\n                                                    const char *email,\n                                                    const char *phone,\n                                                    const char *loc,\n                                                    const char *tool,\n                                                    const char *note);\nORTP_PUBLIC void rtp_session_add_contributing_source(RtpSession *session,\n                                                     uint32_t csrc,\n                                                     const char *cname,\n                                                     const char *name,\n                                                     const char *email,\n                                                     const char *phone,\n                                                     const char *loc,\n                                                     const char *tool,\n                                                     const char *note);\n/* DEPRECATED: Use rtp_session_remove_contributing_source instead of rtp_session_remove_contributing_sources */\n#define rtp_session_remove_contributing_sources rtp_session_remove_contributing_source\nORTP_PUBLIC void rtp_session_remove_contributing_source(RtpSession *session, uint32_t csrc);\nORTP_PUBLIC void rtp_session_clear_contributing_sources(RtpSession *session);\nORTP_PUBLIC mblk_t *rtp_session_create_rtcp_sdes_packet(RtpSession *session, bool_t full);\n\nORTP_PUBLIC void rtp_session_get_last_recv_time(RtpSession *session, struct timeval *tv);\nORTP_PUBLIC int rtp_session_bye(RtpSession *session, const char *reason);\n\nORTP_PUBLIC int rtp_session_get_last_send_error_code(RtpSession *session);\nORTP_PUBLIC void rtp_session_clear_send_error_code(RtpSession *session);\nORTP_PUBLIC int rtp_session_get_last_recv_error_code(RtpSession *session);\nORTP_PUBLIC void rtp_session_clear_recv_error_code(RtpSession *session);\n\nORTP_PUBLIC float rtp_session_get_round_trip_propagation(RtpSession *session);\n\nORTP_PUBLIC void rtp_session_enable_network_simulation(RtpSession *session, const OrtpNetworkSimulatorParams *params);\nORTP_PUBLIC void rtp_session_enable_congestion_detection(RtpSession *session, bool_t enabled);\nORTP_PUBLIC void rtp_session_reset_video_bandwidth_estimator(RtpSession *session);\nORTP_PUBLIC void rtp_session_enable_video_bandwidth_estimator(RtpSession *session,\n                                                              const OrtpVideoBandwidthEstimatorParams *params);\nORTP_PUBLIC void rtp_session_enable_audio_bandwidth_estimator(RtpSession *session,\n                                                              const OrtpAudioBandwidthEstimatorParams *params);\n\nORTP_PUBLIC void rtp_session_rtcp_set_lost_packet_value(RtpSession *session, const int value);\nORTP_PUBLIC void rtp_session_rtcp_set_jitter_value(RtpSession *session, const unsigned int value);\nORTP_PUBLIC void rtp_session_rtcp_set_delay_value(RtpSession *session, const unsigned int value);\nORTP_PUBLIC mblk_t *rtp_session_pick_with_cseq(RtpSession *session, const uint16_t sequence_number);\n\nORTP_PUBLIC void rtp_session_send_rtcp_xr_rcvr_rtt(RtpSession *session);\nORTP_PUBLIC void rtp_session_send_rtcp_xr_dlrr(RtpSession *session);\nORTP_PUBLIC void rtp_session_send_rtcp_xr_stat_summary(RtpSession *session);\nORTP_PUBLIC void rtp_session_send_rtcp_xr_voip_metrics(RtpSession *session);\n\nORTP_PUBLIC bool_t rtp_session_avpf_enabled(RtpSession *session);\nORTP_PUBLIC bool_t rtp_session_avpf_payload_type_feature_enabled(RtpSession *session, unsigned char feature);\nORTP_PUBLIC bool_t rtp_session_avpf_feature_enabled(RtpSession *session, unsigned char feature);\nORTP_PUBLIC void rtp_session_enable_avpf_feature(RtpSession *session, unsigned char feature, bool_t enable);\nORTP_PUBLIC uint16_t rtp_session_get_avpf_rr_interval(RtpSession *session);\nORTP_PUBLIC bool_t rtp_session_rtcp_psfb_scheduled(RtpSession *session, rtcp_psfb_type_t type);\nORTP_PUBLIC bool_t rtp_session_rtcp_rtpfb_scheduled(RtpSession *session, rtcp_rtpfb_type_t type);\nORTP_PUBLIC void rtp_session_send_rtcp_fb_generic_nack(RtpSession *session, uint16_t pid, uint16_t blp);\nORTP_PUBLIC void rtp_session_send_rtcp_fb_pli(RtpSession *session);\nORTP_PUBLIC void rtp_session_send_rtcp_fb_fir(RtpSession *session);\nORTP_PUBLIC void rtp_session_send_rtcp_fb_sli(RtpSession *session, uint16_t first, uint16_t number, uint8_t picture_id);\nORTP_PUBLIC void rtp_session_send_rtcp_fb_rpsi(RtpSession *session, uint8_t *bit_string, uint16_t bit_string_len);\nORTP_PUBLIC void rtp_session_send_rtcp_fb_tmmbr(RtpSession *session, uint64_t mxtbr);\nORTP_PUBLIC void rtp_session_send_rtcp_fb_tmmbn(RtpSession *session, uint32_t ssrc);\nORTP_PUBLIC void rtp_session_send_rtcp_fb_goog_remb(RtpSession *session, uint64_t mxtbr);\n\nORTP_PUBLIC void rtp_session_enable_transfer_mode(RtpSession *session, bool_t enable);\nORTP_PUBLIC bool_t rtp_session_transfer_mode_enabled(RtpSession *session);\n\n/*private */\nORTP_PUBLIC void rtp_session_init(RtpSession *session, RtpSessionMode mode);\n#define rtp_session_set_flag(session, flag) (session)->flags |= (flag)\n#define rtp_session_unset_flag(session, flag) (session)->flags &= ~(flag)\nORTP_PUBLIC void rtp_session_uninit(RtpSession *session);\nORTP_PUBLIC void rtp_session_dispatch_event(RtpSession *session, OrtpEvent *ev);\n\nORTP_PUBLIC void rtp_session_set_reuseaddr(RtpSession *session, bool_t yes);\n\nORTP_PUBLIC int\nmeta_rtp_transport_sendto(RtpTransport *t, mblk_t *msg, int flags, const struct sockaddr *to, socklen_t tolen);\n\nORTP_PUBLIC int\nmeta_rtp_transport_modifier_inject_packet_to_send(RtpTransport *t, RtpTransportModifier *tpm, mblk_t *msg, int flags);\nORTP_PUBLIC int meta_rtp_transport_modifier_inject_packet_to_send_to(\n    RtpTransport *t, RtpTransportModifier *tpm, mblk_t *msg, int flags, const struct sockaddr *to, socklen_t tolen);\nORTP_PUBLIC int\nmeta_rtp_transport_modifier_inject_packet_to_recv(RtpTransport *t, RtpTransportModifier *tpm, mblk_t *msg, int flags);\n\nORTP_PUBLIC int\nmeta_rtp_transport_apply_all_except_one_on_receive(RtpTransport *t, RtpTransportModifier *modifier, mblk_t *msg);\n/**\n * get endpoint if any\n * @param[in] transport RtpTransport object.\n * @return #_RtpTransport\n *\n * */\nORTP_PUBLIC RtpTransport *meta_rtp_transport_get_endpoint(const RtpTransport *transport);\n/**\n * set endpoint\n * @param[in] transport RtpTransport object.\n * @param[in] endpoint RtpEndpoint.\n *\n * */\nORTP_PUBLIC void meta_rtp_transport_set_endpoint(RtpTransport *transport, RtpTransport *endpoint);\n\nORTP_PUBLIC void meta_rtp_transport_destroy(RtpTransport *tp);\nORTP_PUBLIC void meta_rtp_transport_append_modifier(RtpTransport *tp, RtpTransportModifier *tpm);\nORTP_PUBLIC void meta_rtp_transport_prepend_modifier(RtpTransport *tp, RtpTransportModifier *tpm);\nORTP_PUBLIC void meta_rtp_transport_remove_modifier(RtpTransport *tp, RtpTransportModifier *tpm);\n\nORTP_PUBLIC int rtp_session_splice(RtpSession *session, RtpSession *to_session);\nORTP_PUBLIC int rtp_session_unsplice(RtpSession *session, RtpSession *to_session);\n\nORTP_PUBLIC bool_t ortp_stream_is_ipv6(OrtpStream *os);\n\n/* RtpBundle api */\n#define RTP_BUNDLE_MAX_SENT_MID_START 10\n#define RTP_BUNDLE_MID_SENDING_INTERVAL 1000\n\ntypedef struct _RtpBundle RtpBundle;\n\nORTP_PUBLIC RtpBundle *rtp_bundle_new(void);\nORTP_PUBLIC void rtp_bundle_delete(RtpBundle *bundle);\n\nORTP_PUBLIC int rtp_bundle_get_mid_extension_id(RtpBundle *bundle);\nORTP_PUBLIC void rtp_bundle_set_mid_extension_id(RtpBundle *bundle, int id);\n\nORTP_PUBLIC void rtp_bundle_add_session(RtpBundle *bundle, const char *mid, RtpSession *session);\n\nORTP_PUBLIC void rtp_bundle_remove_sessions_by_id(RtpBundle *bundle, const char *mid);\nORTP_PUBLIC void rtp_bundle_remove_session(RtpBundle *bundle, RtpSession *session);\nORTP_PUBLIC void rtp_bundle_clear(RtpBundle *bundle);\n\nORTP_PUBLIC RtpSession *rtp_bundle_get_primary_session(RtpBundle *bundle);\nORTP_PUBLIC void rtp_bundle_set_primary_session(RtpBundle *bundle, RtpSession *session);\n\nORTP_PUBLIC char *rtp_bundle_get_session_mid(RtpBundle *bundle, RtpSession *session);\n\nORTP_PUBLIC int rtp_bundle_send_through_primary(\n    RtpBundle *bundle, bool_t is_rtp, mblk_t *m, int flags, const struct sockaddr *destaddr, socklen_t destlen);\n\n/**\n * @brief Dispatch a received packet through the bundle\n *\n * @param[in]\tbundle\tThe bundle holding the rtp sessions\n * @param[in]\tis_rtp\tThe type of the packet, RTP or RTCP\n * @param[in]\tm\tThe packet to dispatch\n *\n * @return\tthe packet at destination of the primary session, NULL if there is none\n */\nORTP_PUBLIC mblk_t *rtp_bundle_dispatch(RtpBundle *bundle, bool_t is_rtp, mblk_t *m);\n\n/**\n * @brief Retrieve a session from a bundle using an outgoing message\n *\n * @param[in]\tbundle\tThe bundle holding the rtp sessions\n * @param[in]\tm\tThe outgoing message\n * @return\tthe Rtp session used to send this message, NULL if not found in the bundle\n *\n * Warning: this function current implementation assumes a match MID/SSRC, it may change\n */\nORTP_PUBLIC RtpSession *rtp_bundle_lookup_session_for_outgoing_packet(RtpBundle *bundle, mblk_t *m);\nORTP_PUBLIC void\nrtp_session_use_local_addr(RtpSession *session, const char *rtp_local_addr, const char *rtcp_local_addr);\n\ntypedef struct _FecStream FecStream;\ntypedef struct _FecParams FecParams;\n\ntypedef struct fec_stats_t {\n\tuint64_t col_repair_sent;\n\tuint64_t col_repair_received;\n\tuint64_t row_repair_sent;\n\tuint64_t row_repair_received;\n\tuint64_t packets_lost;          // number of source packets lost during the call, that can be repaired by FEC or not\n\tuint64_t packets_not_recovered; // number of source packets lost and not repaired\n\tuint64_t packets_recovered;     // number of source packets lost and repaired by FEC\n} fec_stats;\n\nORTP_PUBLIC FecParams *fec_params_new(uint32_t repairWindow);\nORTP_PUBLIC void fec_params_destroy(FecParams *params);\nORTP_PUBLIC void fec_params_update(FecParams *params, uint8_t level);\nORTP_PUBLIC uint8_t fec_params_estimate_best_level(\n    FecParams *params, float loss_rate, int bitrate, float current_overhead, float *estimated_overhead);\n\nORTP_PUBLIC FecStream *fec_stream_new(struct _RtpSession *source, struct _RtpSession *fec, FecParams *fecParams);\nORTP_PUBLIC void fec_stream_destroy(FecStream *fec_stream);\nORTP_PUBLIC void fec_stream_unsubscribe(FecStream *fec_stream, FecParams *fecParams);\nORTP_PUBLIC void fec_stream_reset_cluster(FecStream *fec_stream);\nORTP_PUBLIC void fec_stream_receive_repair_packet(FecStream *fec_stream, uint32_t timestamp);\nORTP_PUBLIC mblk_t *fec_stream_find_missing_packet(FecStream *fec_stream, uint16_t seqnum);\nORTP_PUBLIC RtpSession *fec_stream_get_fec_session(FecStream *fec_stream);\nORTP_PUBLIC void fec_stream_count_lost_packets(FecStream *fec_stream, uint16_t seqnum, int16_t diff);\nORTP_PUBLIC void fec_stream_print_stats(FecStream *fec_stream);\nORTP_PUBLIC fec_stats *fec_stream_get_stats(FecStream *fec_stream);\nORTP_PUBLIC bool_t fec_stream_enabled(FecStream *fec_stream);\nORTP_PUBLIC float fec_stream_get_overhead(FecStream *fec_stream);\nORTP_PUBLIC void fec_stream_reset_overhead_measure(FecStream *fec_stream);\n\n/* Audio Bandwidth Estimator stats */\ntypedef struct abe_stats {\n\tuint32_t sent_dup; /**< Number of duplicated packet generated */\n\tuint32_t recv_dup; /**< Number of duplicated packet received(this includes only the one used to estimate\n\t                      bandwidth) */\n} abe_stats_t;\n\n/**\n * Get the stats from audio bandwidth estimator if any\n * @param[in]\tsession\t\tThe RtpSession holding the ABE\n *\n * @return the ABE stats, NULL if no ABE exists in the session\n */\nORTP_PUBLIC const abe_stats_t *rtp_session_get_audio_bandwidth_estimator_stats(RtpSession *session);\n\n/**\n * Get the current duplication rate for the audio bandwidth estimator\n * @param[in]\tsession\t\tThe RtpSession holding the ABE\n *\n * @return the duplicate rate used by ABE, -1 if no ABE exists in the session\n */\nORTP_PUBLIC int rtp_session_get_audio_bandwidth_estimator_duplicate_rate(RtpSession *session);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/rtpsignaltable.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef rtpsignaltable_h\n#define rtpsignaltable_h\n\n#include <bctoolbox/port.h>\n\n#define RTP_CALLBACK_TABLE_MAX_ENTRIES 50\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void (*RtpCallback)(struct _RtpSession *, void *arg1, void *arg2, void *arg3);\n\nstruct _RtpSignalCallback {\n\tRtpCallback cb;\n\tvoid *user_data;\n\n\t// Callbacks can be added from other sessions (e.g. bundle).\n\t// If so, keep a reference to it so we can remove it easily.\n\tconst struct _RtpSession *source;\n};\n\ntypedef struct _RtpSignalCallback RtpSignalCallback;\n\nstruct _RtpSignalTable {\n\tRtpSignalCallback callback[RTP_CALLBACK_TABLE_MAX_ENTRIES];\n\tbctbx_mutex_t callback_mutex;\n\tstruct _RtpSession *session;\n\tconst char *signal_name;\n\tint count;\n};\n\ntypedef struct _RtpSignalTable RtpSignalTable;\n\nvoid rtp_signal_table_init(RtpSignalTable *table, struct _RtpSession *session, const char *signal_name);\n\nvoid rtp_signal_table_uninit(RtpSignalTable *table);\n\nint rtp_signal_table_add(RtpSignalTable *table, RtpCallback cb, void *user_data);\n\nint rtp_signal_table_add_from_source_session(RtpSignalTable *table,\n                                             RtpCallback cb,\n                                             void *user_data,\n                                             const struct _RtpSession *source);\n\nvoid rtp_signal_table_emit(RtpSignalTable *table);\n\n/* emit but with a second arg */\nvoid rtp_signal_table_emit2(RtpSignalTable *table, void *arg);\n\n/* emit but with a third arg */\nvoid rtp_signal_table_emit3(RtpSignalTable *table, void *arg1, void *arg2);\n\nint rtp_signal_table_remove_by_callback(RtpSignalTable *table, RtpCallback cb);\n\nint rtp_signal_table_remove_by_callback_and_user_data(RtpSignalTable *table, RtpCallback cb, void *user_data);\n\nint rtp_signal_table_remove_by_source_session(RtpSignalTable *table, const struct _RtpSession *session);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/sessionset.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n/**\n * \\file sessionset.h\n * \\brief Sending and receiving multiple streams together with only one thread.\n *\n **/\n#ifndef SESSIONSET_H\n#define SESSIONSET_H\n\n#include <ortp/rtpsession.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#if !defined(_WIN32) && !defined(_WIN32_WCE)\n/* UNIX */\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#define ORTP_FD_SET(d, s) FD_SET(d, s)\n#define ORTP_FD_CLR(d, s) FD_CLR(d, s)\n#define ORTP_FD_ISSET(d, s) FD_ISSET(d, s)\n#define ORTP_FD_ZERO(s) FD_ZERO(s)\n\ntypedef fd_set ortp_fd_set;\n\n#else\n/* _WIN32 */\n\n#define ORTP_FD_ZERO(s)                                                                                                \\\n\tdo {                                                                                                               \\\n\t\tunsigned int __i;                                                                                              \\\n\t\tortp_fd_set *__arr = (s);                                                                                      \\\n\t\tfor (__i = 0; __i < sizeof(ortp_fd_set) / sizeof(ortp__fd_mask); ++__i)                                        \\\n\t\t\tORTP__FDS_BITS(__arr)[__i] = 0;                                                                            \\\n\t} while (0)\n#define ORTP_FD_SET(d, s) (ORTP__FDS_BITS(s)[ORTP__FDELT(d)] |= ORTP__FDMASK(d))\n#define ORTP_FD_CLR(d, s) (ORTP__FDS_BITS(s)[ORTP__FDELT(d)] &= ~ORTP__FDMASK(d))\n#define ORTP_FD_ISSET(d, s) ((ORTP__FDS_BITS(s)[ORTP__FDELT(d)] & ORTP__FDMASK(d)) != 0)\n\n/* The fd_set member is required to be an array of longs.  */\ntypedef long int ortp__fd_mask;\n\n/* Number of bits per word of `fd_set' (some code assumes this is 32).  */\n#define ORTP__FD_SETSIZE 1024\n\n/* It's easier to assume 8-bit bytes than to get CHAR_BIT.  */\n#define ORTP__NFDBITS (8 * sizeof(ortp__fd_mask))\n#define ORTP__FDELT(d) ((d) / ORTP__NFDBITS)\n#define ORTP__FDMASK(d) ((ortp__fd_mask)1 << ((d) % ORTP__NFDBITS))\n\n/* fd_set for select and pselect.  */\ntypedef struct {\n\tortp__fd_mask fds_bits[ORTP__FD_SETSIZE / ORTP__NFDBITS];\n#define ORTP__FDS_BITS(set) ((set)->fds_bits)\n} ortp_fd_set;\n\n#endif /*end _WIN32*/\n\nstruct _SessionSet {\n\tortp_fd_set rtpset;\n};\n\ntypedef struct _SessionSet SessionSet;\n\n#define session_set_init(ss) ORTP_FD_ZERO(&(ss)->rtpset)\n\nORTP_PUBLIC SessionSet *session_set_new(void);\n/**\n * This macro adds the rtp session to the set.\n * @param ss a set (SessionSet object)\n * @param rtpsession a RtpSession\n **/\n#define session_set_set(ss, rtpsession) ORTP_FD_SET((rtpsession)->mask_pos, &(ss)->rtpset)\n\n/**\n * This macro tests if the session is part of the set. 1 is returned if true, 0 else.\n *@param ss a set\n *@param rtpsession a rtp session\n *\n **/\n#define session_set_is_set(ss, rtpsession) ORTP_FD_ISSET((rtpsession)->mask_pos, &(ss)->rtpset)\n\n/**\n * Removes the session from the set.\n *@param ss a set of sessions.\n *@param rtpsession a rtp session.\n *\n *\n **/\n#define session_set_clr(ss, rtpsession) ORTP_FD_CLR((rtpsession)->mask_pos, &(ss)->rtpset)\n\n#define session_set_copy(dest, src) memcpy(&(dest)->rtpset, &(src)->rtpset, sizeof(ortp_fd_set))\n\n/**\n * Frees a SessionSet.\n **/\nORTP_PUBLIC void session_set_destroy(SessionSet *set);\n\nORTP_PUBLIC int session_set_select(SessionSet *recvs, SessionSet *sends, SessionSet *errors);\nORTP_PUBLIC int\nsession_set_timedselect(SessionSet *recvs, SessionSet *sends, SessionSet *errors, struct timeval *timeout);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/str_utils.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef STR_UTILS_H\n#define STR_UTILS_H\n\n#include <ortp/port.h>\n#if defined(ORTP_TIMESTAMP)\n#include <time.h>\n#endif\n\n#ifndef MIN\n#define MIN(a, b) (((a) > (b)) ? (b) : (a))\n#endif\n#ifndef MAX\n#define MAX(a, b) (((a) > (b)) ? (a) : (b))\n#endif\n\n#define return_if_fail(expr)                                                                                           \\\n\tif (!(expr)) {                                                                                                     \\\n\t\tprintf(\"%s:%i- assertion\" #expr \"failed\\n\", __FILE__, __LINE__);                                               \\\n\t\treturn;                                                                                                        \\\n\t}\n#define return_val_if_fail(expr, ret)                                                                                  \\\n\tif (!(expr)) {                                                                                                     \\\n\t\tprintf(\"%s:%i- assertion\" #expr \"failed\\n\", __FILE__, __LINE__);                                               \\\n\t\treturn (ret);                                                                                                  \\\n\t}\n\ntypedef struct ortp_recv_addr {\n\tint family;\n\tunion {\n\t\tstruct in_addr ipi_addr;\n\t\tstruct in6_addr ipi6_addr;\n\t} addr;\n\tunsigned short port;\n} ortp_recv_addr_t;\n\ntypedef struct ortp_recv_addr_map {\n\tstruct sockaddr_storage ss;\n\tortp_recv_addr_t recv_addr;\n\tuint64_t ts;\n} ortp_recv_addr_map_t;\n\ntypedef struct msgb {\n\tstruct msgb *b_prev;\n\tstruct msgb *b_next;\n\tstruct msgb *b_cont;\n\tstruct datab *b_datap;\n\tunsigned char *b_rptr;\n\tunsigned char *b_wptr;\n\tuint32_t reserved1;\n\tuint32_t reserved2;\n\tstruct timeval timestamp;\n\tortp_recv_addr_t recv_addr;       /*contains the destination address of incoming packets, used for ICE processing*/\n\tstruct sockaddr_storage net_addr; /*source address of incoming packet, or dest address of outgoing packet, used only\n\t                                     by simulator and modifiers*/\n\tsocklen_t\n\t    net_addrlen; /*source (dest) address of incoming (outgoing) packet length used by simulator and modifiers*/\n\tuint8_t ttl_or_hl;\n} mblk_t;\n\n// Data Block\ntypedef struct datab {\n\tunsigned char *db_base;\n\tunsigned char *db_lim;\n\tvoid (*db_freefn)(void *);\n\tvoid *db_ref; // Atomic variable\n} dblk_t;\n\ntypedef struct _queue {\n\tmblk_t _q_stopper;\n\tint q_mcount; /*number of packet in the q */\n} queue_t;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nORTP_PUBLIC dblk_t *dblk_alloc(size_t size);\nORTP_PUBLIC dblk_t *dblk_alloc2(uint8_t *buf, size_t size, void (*freefn)(void *));\nORTP_PUBLIC void dblk_ref(dblk_t *d);\nORTP_PUBLIC void dblk_unref(dblk_t *d);\nORTP_PUBLIC int dblk_ref_value(dblk_t *db);\nORTP_PUBLIC unsigned char *dblk_base(dblk_t *db);\nORTP_PUBLIC unsigned char *dblk_lim(dblk_t *db);\n\nORTP_PUBLIC void qinit(queue_t *q);\n\nORTP_PUBLIC void putq(queue_t *q, mblk_t *m);\n\nORTP_PUBLIC mblk_t *getq(queue_t *q);\n\nORTP_PUBLIC void insq(queue_t *q, mblk_t *emp, mblk_t *mp);\n\nORTP_PUBLIC void remq(queue_t *q, mblk_t *mp);\n\nORTP_PUBLIC mblk_t *peekq(queue_t *q);\n\n/* remove and free all messages in the q */\n#define FLUSHALL 0\nORTP_PUBLIC void flushq(queue_t *q, int how);\n\nORTP_PUBLIC void mblk_init(mblk_t *mp);\n\nORTP_PUBLIC void mblk_meta_copy(const mblk_t *source, mblk_t *dest);\n\n/* allocates a mblk_t, that points to a datab_t, that points to a buffer of size size. */\nORTP_PUBLIC mblk_t *allocb(size_t size, int unused);\n#define BPRI_MED 0\n\n/* allocates a mblk_t, that points to a datab_t, that points to buf; buf will be freed using freefn */\nORTP_PUBLIC mblk_t *esballoc(uint8_t *buf, size_t size, int pri, void (*freefn)(void *));\n\n/* frees a mblk_t, and if the datab ref_count is 0, frees it and the buffer too */\nORTP_PUBLIC void freeb(mblk_t *m);\n\n/* frees recursively (follow b_cont) a mblk_t, and if the datab\nref_count is 0, frees it and the buffer too */\nORTP_PUBLIC void freemsg(mblk_t *mp);\n\n/* duplicates a mblk_t , buffer is not duplicated*/\nORTP_PUBLIC mblk_t *dupb(mblk_t *m);\n\n/* duplicates a complex mblk_t, buffer is not duplicated */\nORTP_PUBLIC mblk_t *dupmsg(mblk_t *m);\n\n/* returns the size of data of a message */\nORTP_PUBLIC size_t msgdsize(const mblk_t *mp);\n\n/* concatenates all fragment of a complex message and crop or extend the buffer to the given length */\nORTP_PUBLIC void msgpullup(mblk_t *mp, size_t len);\n\n/* concatenates all fragment of a complex message and insert an empty buffer of the given length at the given offset */\nORTP_PUBLIC void msgpullup_with_insert(mblk_t *mp, size_t offset, size_t len);\n\n/* duplicates a single message, but with buffer included */\nORTP_PUBLIC mblk_t *copyb(const mblk_t *mp);\n\n/* duplicates a complex message with buffer included */\nORTP_PUBLIC mblk_t *copymsg(const mblk_t *mp);\n\nORTP_PUBLIC mblk_t *appendb(mblk_t *mp, const char *data, size_t size, bool_t pad);\nORTP_PUBLIC void msgappend(mblk_t *mp, const char *data, size_t size, bool_t pad);\n\nORTP_PUBLIC mblk_t *concatb(mblk_t *mp, mblk_t *newm);\n\n/*Make sure the message has a unique owner, if not duplicate the underlying data buffer so that it can be changed\n without impacting others. Note that in case of copy, the message will be un-fragmented, exactly the way msgpullup()\n does. Always returns mp.*/\nORTP_PUBLIC mblk_t *msgown(mblk_t *mp);\n\n#define qempty(q) (&(q)->_q_stopper == (q)->_q_stopper.b_next)\n#define qfirst(q) ((q)->_q_stopper.b_next != &(q)->_q_stopper ? (q)->_q_stopper.b_next : NULL)\n#define qbegin(q) ((q)->_q_stopper.b_next)\n#define qlast(q) ((q)->_q_stopper.b_prev != &(q)->_q_stopper ? (q)->_q_stopper.b_prev : NULL)\n#define qend(q, mp) ((mp) == &(q)->_q_stopper)\n#define qnext(q, mp) ((mp)->b_next)\n\ntypedef struct _msgb_allocator {\n\tqueue_t q;\n\tint max_blocks;\n} msgb_allocator_t;\n\nORTP_PUBLIC void msgb_allocator_init(msgb_allocator_t *pa);\n/* Set a maximum number of blocks that can be managed by the allocator.\n   Only blocks satisfying the \"size\" argument of msgb_allocator_alloc() are counted.*/\nORTP_PUBLIC void msgb_allocator_set_max_blocks(msgb_allocator_t *pa, int max_blocks);\nORTP_PUBLIC mblk_t *msgb_allocator_alloc(msgb_allocator_t *pa, size_t size);\nORTP_PUBLIC void msgb_allocator_uninit(msgb_allocator_t *pa);\n\nORTP_PUBLIC void ortp_recvaddr_to_sockaddr(ortp_recv_addr_t *recvaddr, struct sockaddr *addr, socklen_t *socklen);\nORTP_PUBLIC void ortp_sockaddr_to_recvaddr(const struct sockaddr *addr, ortp_recv_addr_t *recvaddr);\n\n/* API to store retrieve a sequence number, payload type, ekt tag flag and netsim rtp flag in the packet\n * These informations are used by double encryption to store the original seqnum/pt in the paquet\n * and allows ms2 to force the full ekt on specific video frames.\n *\n * The information is stored in reserved1. reserved1 is also used by netsim. The mapping is:\n * 32        24          16          8         1\n * | RuuuuuuE |   PType  |    Sequence number  |\n * With u unused, R netsim RTP flag, E force EKT tag flag\n * reserved1 is \"locked\" by the seqnum/pt from the begining of rtp_session_send_with_ts\n * until all the modifiers are passed (SRTP needs this info and is the last modifier) */\n#define ortp_mblk_set_original_seqnum(m, seqnum) (m)->reserved1 = ((m)->reserved1 & 0xFFFF0000) | (seqnum & 0x0000FFFF)\n#define ortp_mblk_get_original_seqnum(m) (((m)->reserved1) & 0x0000FFFF)\n\n#define ortp_mblk_set_original_pt(m, pt) (m)->reserved1 = ((m)->reserved1 & 0xFF00FFFF) | ((pt & 0x000000FF) << 16)\n#define ortp_mblk_get_original_pt(m) ((((m)->reserved1) & 0x00FF0000) >> 16)\n\n#define ortp_mblk_set_ekt_tag_flag(m, f) (m)->reserved1 = ((m)->reserved1 & 0xFEFFFFFF) | ((f & 0x00000001) << 24)\n#define ortp_mblk_get_ekt_tag_flag(m) ((((m)->reserved1) & 0x01000000) >> 24)\n\n#define ortp_mblk_set_netsim_is_rtp_flag(m, f) (m)->reserved1 = ((m)->reserved1 & 0x7FFFFFFF) | ((f & 0x00000001) << 31)\n#define ortp_mblk_get_netsim_is_rtp_flag(m) ((((m)->reserved1) & 0x80000000) >> 31)\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/ortp/telephonyevents.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/**\n * \\file telephonyevents.h\n * \\brief Receiving and sending telephone events (RFC2833)\n *\n **/\n\n#ifndef TELEPHONYEVENTS_H\n#define TELEPHONYEVENTS_H\n\n#include <ortp/rtpsession.h>\n\nstruct _telephone_event {\n#ifdef ORTP_BIGENDIAN\n\tuint32_t event : 8;\n\tuint32_t E : 1;\n\tuint32_t R : 1;\n\tuint32_t volume : 6;\n\tuint32_t duration : 16;\n#else\n\tuint32_t event : 8;\n\tuint32_t volume : 6;\n\tuint32_t R : 1;\n\tuint32_t E : 1;\n\tuint32_t duration : 16;\n#endif\n};\n\ntypedef struct _telephone_event telephone_event_t;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* tell if the session supports telephony events. For this the telephony events payload_type\n    must be present in the rtp profile used by the session */\n\n/* low level functions */\nORTP_PUBLIC int rtp_session_telephone_events_supported(RtpSession *session);\nORTP_PUBLIC int rtp_session_send_telephone_events_supported(RtpSession *session);\nORTP_PUBLIC int rtp_session_recv_telephone_events_supported(RtpSession *session);\nORTP_PUBLIC mblk_t *rtp_session_create_telephone_event_packet(RtpSession *session, int start);\n\nORTP_PUBLIC int rtp_session_add_telephone_event(\n    RtpSession *session, mblk_t *packet, uint8_t event, int end, uint8_t volume, uint16_t duration);\n\nORTP_PUBLIC int rtp_session_read_telephone_event(RtpSession *session, mblk_t *packet, telephone_event_t **tab);\n\n/* high level functions*/\nORTP_PUBLIC int rtp_session_send_dtmf(RtpSession *session, char dtmf, uint32_t userts);\nORTP_PUBLIC int rtp_session_send_dtmf2(RtpSession *session, char dtmf, uint32_t userts, int duration);\n/* for high level telephony event callback */\nORTP_PUBLIC void rtp_session_check_telephone_events(RtpSession *session, mblk_t *m0);\n\n#ifdef __cplusplus\n}\n#endif\n\n/* the size allocated for telephony events packets */\n#define TELEPHONY_EVENTS_ALLOCATED_SIZE (4 * sizeof(telephone_event_t))\n\n/* list of named events */\n#define TEV_DTMF_0 (0)\n#define TEV_DTMF_1 (1)\n#define TEV_DTMF_2 (2)\n#define TEV_DTMF_3 (3)\n#define TEV_DTMF_4 (4)\n#define TEV_DTMF_5 (5)\n#define TEV_DTMF_6 (6)\n#define TEV_DTMF_7 (7)\n#define TEV_DTMF_8 (8)\n#define TEV_DTMF_9 (9)\n#define TEV_DTMF_STAR (10)\n#define TEV_DTMF_POUND (11)\n#define TEV_DTMF_A (12)\n#define TEV_DTMF_B (13)\n#define TEV_DTMF_C (14)\n#define TEV_DTMF_D (15)\n#define TEV_FLASH (16)\n\n#endif\n"
  },
  {
    "path": "include/ortp/utils.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef ORTP_UTILS_H\n#define ORTP_UTILS_H\n\n#include \"ortp/port.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Utility object to determine a maximum or minimum (but not both at the same\n * time), of a signal during a sliding period of time.\n */\ntypedef struct _OrtpExtremum {\n\tfloat current_extremum;\n\tfloat last_stable;\n\tuint64_t extremum_time;\n\tint period;\n} OrtpExtremum;\nORTP_PUBLIC void ortp_extremum_reset(OrtpExtremum *obj);\nORTP_PUBLIC void ortp_extremum_init(OrtpExtremum *obj, int period);\n/**\n * Record a new value for minimal.\n * @return TRUE if extremum has changed or false otherwise.\n */\nORTP_PUBLIC bool_t ortp_extremum_record_min(OrtpExtremum *obj, uint64_t curtime, float value);\nORTP_PUBLIC bool_t ortp_extremum_record_max(OrtpExtremum *obj, uint64_t curtime, float value);\nORTP_PUBLIC float ortp_extremum_get_current(OrtpExtremum *obj);\n/**\n * Unlike ortp_extremum_get_current() which can be very fluctuating, ortp_extremum_get_previous() returns the extremum\n *found for the previous period.\n **/\nORTP_PUBLIC float ortp_extremum_get_previous(OrtpExtremum *obj);\n\n/**\n * Utility object to interpolate linear model based on captures with 0-mean noise.\n * Based on the model (x, y) where y evolves depending on x with relation y = m*x+b,\n * it will estimate unknown values b, m using given noisy measures xmes and ymes, eg real\n * system is evolving with: y = m * x + b + noise.\n * Note: If noise is NOT white, the residue will be absorbed by one of the estimators.\n * It is an implementation of recursive least square algorithm, based on Kalman filter.\n */\ntypedef struct _OrtpKalmanRLS {\n\t/* Unknown parameters to estimate */\n\tdouble m, b;\n\t/* Gain matrix, must not be modified */\n\tdouble P[2][2];\n\t/** Forgetting factor in [94, .999]. Used when unknown parameters vary in time. **/\n\tdouble lambda;\n} OrtpKalmanRLS;\n\nORTP_PUBLIC void ortp_kalman_rls_init(OrtpKalmanRLS *obj, double m0, double b0);\nORTP_PUBLIC void ortp_kalman_rls_record(OrtpKalmanRLS *obj, double xmes, double ymes);\n\n/**\n * Object to compute bandwidth of incoming or outgoing streams.\n * Two variants are proposed for short-term (1 sec), or long term (3 secs).\n * The computation by doing a sliding average over the considered time interval\n **/\ntypedef struct _OrtpBandwidthMeasurer OrtpBandwidthMeasurer;\n\nORTP_PUBLIC OrtpBandwidthMeasurer *ortp_bandwidth_measurer_long_term_new(void);\n\nORTP_PUBLIC OrtpBandwidthMeasurer *ortp_bandwidth_measurer_short_term_new(void);\n\nORTP_PUBLIC void ortp_bandwidth_measurer_add_bytes(OrtpBandwidthMeasurer *obj, size_t bytes, const struct timeval *t);\n\nORTP_PUBLIC float ortp_bandwidth_measurer_get_bandwdith(OrtpBandwidthMeasurer *obj);\n\nORTP_PUBLIC void ortp_bandwidth_measurer_destroy(OrtpBandwidthMeasurer *obj);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "m4/Makefile.am",
    "content": "EXTRA_DIST=\\\n\tobsolete.m4\n"
  },
  {
    "path": "m4/ld-output-def.m4",
    "content": "# ld-output-def.m4 serial 2\ndnl Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.\ndnl This file is free software; the Free Software Foundation\ndnl gives unlimited permission to copy and/or distribute it,\ndnl with or without modifications, as long as this notice is preserved.\n\ndnl From Simon Josefsson\n\n# gl_LD_OUTPUT_DEF()\n# -------------\n# Check if linker supports -Wl,--output-def and define automake\n# conditional HAVE_LD_OUTPUT_DEF if it is.\nAC_DEFUN([gl_LD_OUTPUT_DEF],\n[\n  AC_CACHE_CHECK([if gcc/ld supports -Wl,--output-def],\n    [gl_cv_ld_output_def],\n    [if test \"$enable_shared\" = no; then\n       gl_cv_ld_output_def=\"not needed, shared libraries are disabled\"\n     else\n       gl_ldflags_save=$LDFLAGS\n       LDFLAGS=\"-Wl,--output-def,conftest.def\"\n       AC_LINK_IFELSE([AC_LANG_PROGRAM([])],\n                   [gl_cv_ld_output_def=yes],\n                   [gl_cv_ld_output_def=no])\n       rm -f conftest.def\n       LDFLAGS=\"$gl_ldflags_save\"\n     fi])\n  AM_CONDITIONAL([HAVE_LD_OUTPUT_DEF], test \"x$gl_cv_ld_output_def\" = \"xyes\")\n])\n"
  },
  {
    "path": "m4/obsolete.m4",
    "content": "AC_DEFUN([AM_PROG_MKDIR_P], [AC_PROG_MKDIR_P([$@])])\n"
  },
  {
    "path": "oRTP.prj",
    "content": "# Anjuta Version 1.1.97 \nCompatibility Level: 1 \n\n<PROJECT_DESCRIPTION_START>\nSome description<PROJECT_DESCRIPTION_END>\n<CONFIG_PROGS_START>\n<CONFIG_PROGS_END>\n<CONFIG_LIBS_START>\n<CONFIG_LIBS_END>\n<CONFIG_HEADERS_START>\n<CONFIG_HEADERS_END>\n<CONFIG_CHARACTERISTICS_START>\n<CONFIG_CHARACTERISTICS_END>\n<CONFIG_LIB_FUNCS_START>\n<CONFIG_LIB_FUNCS_END>\n<CONFIG_ADDITIONAL_START>\n<CONFIG_ADDITIONAL_END>\n<CONFIG_FILES_START>\n<CONFIG_FILES_END>\n<MAKEFILE_AM_START>\n<MAKEFILE_AM_END>\n\nprops.file.type=project\n\nanjuta.version=1.1.97\nanjuta.compatibility.level=1\n\nproject.name=oRTP\nproject.type=GENERIC\nproject.target.type=EXECUTABLE\nproject.version=0.99\nproject.author=Simon Morlat\nproject.source.target=Rtp stack\nproject.has.gettext=0\nproject.gui.command=\nproject.programming.language=C\nproject.excluded.modules=intl\n\nproject.config.extra.modules.before=\nproject.config.extra.modules.after=\nproject.config.blocked=1\nproject.config.disable.overwriting=1 1 1 1 1 1 1 1 1 \n\nproject.menu.entry=oRTP Version 0.99\nproject.menu.group=Application\nproject.menu.comment=oRTP Version 0.99\nproject.menu.icon=\nproject.menu.need.terminal=0\n\nproject.configure.options=\nanjuta.program.arguments=\npreferences.build.option.jobs=0\npreferences.build.option.silent=0\npreferences.build.option.autosave=1\npreferences.anjuta.make.options=-k\npreferences.make=make\npreferences.build.option.keep.going=1\npreferences.build.option.warn.undef=0\npreferences.autoformat.custom.style= -i8 -sc -bli0 -bl0 -cbi0 -ss\npreferences.autoformat.style=Style of Kangleipak\npreferences.indent.opening=0\npreferences.autoformat.disable=0\npreferences.indent.automatic=1\npreferences.use.tabs=1\npreferences.indent.size=4\npreferences.tabsize=4\npreferences.indent.closing=0\n\nmodule.include.name=.\nmodule.include.type=\nmodule.include.files=\\\n\tsrc/errno-win32.h\\\n\tsrc/export.h\\\n\tsrc/ortp-config-win32.h\\\n\tsrc/ortp.h\\\n\tsrc/payloadtype.h\\\n\tsrc/port_fct.h\\\n\tsrc/rtp.h\\\n\tsrc/rtpmod.h\\\n\tsrc/rtpport.h\\\n\tsrc/rtpsession.h\\\n\tsrc/rtpsignaltable.h\\\n\tsrc/rtptimer.h\\\n\tsrc/scheduler.h\\\n\tsrc/sessionset.h\\\n\tsrc/str_utils.h\\\n\tsrc/telephonyevents.h\\\n\tsrc/rtcp.h\\\n\tortp-config.h\n\nmodule.source.name=.\nmodule.source.type=\nmodule.source.files=\\\n\tsrc/avprofile.c\\\n\tsrc/export.c\\\n\tsrc/mrtprecv.c\\\n\tsrc/mrtpsend.c\\\n\tsrc/ortp.c\\\n\tsrc/ortpdlkm.c\\\n\tsrc/payloadtype.c\\\n\tsrc/port_fct.c\\\n\tsrc/posixtimer.c\\\n\tsrc/rtpmemtest.c\\\n\tsrc/rtpmod.c\\\n\tsrc/rtpparse.c\\\n\tsrc/rtprecv.c\\\n\tsrc/rtpsend.c\\\n\tsrc/rtpsession.c\\\n\tsrc/rtpsignaltable.c\\\n\tsrc/rtptimer.c\\\n\tsrc/scheduler.c\\\n\tsrc/sessionset.c\\\n\tsrc/str_utils.c\\\n\tsrc/telephonyevents.c\\\n\tsrc/test_tevrecv.c\\\n\tsrc/test_tevsend.c\\\n\tsrc/test_timer.c\\\n\tsrc/tevmrtprecv.c\\\n\tsrc/tevrtprecv.c\\\n\tsrc/tevrtpsend.c\n\nmodule.pixmap.name=.\nmodule.pixmap.type=\nmodule.pixmap.files=\\\n\tdocs/html/home.png\\\n\tdocs/html/left.png\\\n\tdocs/html/right.png\\\n\tdocs/html/up.png\n\nmodule.data.name=.\nmodule.data.type=\nmodule.data.files=\n\nmodule.help.name=.\nmodule.help.type=\nmodule.help.files=\n\nmodule.doc.name=.\nmodule.doc.type=\nmodule.doc.files=\\\n\tbuild/win32/oRTP/README\\\n\tAUTHORS\\\n\tCOPYING\\\n\tChangeLog\\\n\tINSTALL\\\n\tNEWS\\\n\tREADME\\\n\tTODO\\\n\tdocs/tmpl/multiplexing.sgml\\\n\tdocs/tmpl/ortp-unused.sgml\\\n\tdocs/tmpl/payloads.sgml\\\n\tdocs/tmpl/rtpsessionapi.sgml\\\n\tdocs/tmpl/stackinit.sgml\\\n\tdocs/tmpl/stackmanagement.sgml\\\n\tdocs/tmpl/telephoneevents.sgml\\\n\tdocs/sgml/payloads.sgml\\\n\tdocs/sgml/tree_index.sgml\\\n\tdocs/sgml/object_index.sgml\\\n\tdocs/sgml/rtpsessionapi.sgml\\\n\tdocs/sgml/stackmanagement.sgml\\\n\tdocs/sgml/multiplexing.sgml\\\n\tdocs/sgml/telephoneevents.sgml\\\n\tdocs/ortp-docs.sgml\\\n\tdocs/html/book1.html\\\n\tdocs/html/ortpapi.html\\\n\tdocs/html/ortp-stack-management-functions.html\\\n\tdocs/html/ortp-rtpsession-api.html\\\n\tdocs/html/ortp-rtp-payloads-and-profiles.html\\\n\tdocs/html/ortp-multiplexing-sessions-(in-a-one-thread-design).html\\\n\tdocs/html/ortp-telephone-events-(rfc2833)-.html\\\n\tdocs/html/index.sgml\\\n\tdocs/html/ortp-library-management-functions.html\n\nmodule.po.files=\n\ncompiler.options.supports=\ncompiler.options.include.paths=\\\n\t.\\\n\t..\ncompiler.options.library.paths=\ncompiler.options.libraries=\ncompiler.options.libraries.selected=\ncompiler.options.defines=\\\n\tHAVE_CONFIG_H\ncompiler.options.defines.selected=\ncompiler.options.warning.buttons=0 0 1 1 0 1 0 0 0 0 0 0 0 1 0 0 \ncompiler.options.optimize.buttons=0 0 1 0 \ncompiler.options.other.buttons=1 0 \ncompiler.options.other.c.flags=\ncompiler.options.other.l.flags=\ncompiler.options.other.l.libs=\n\nproject.src.paths=\n"
  },
  {
    "path": "oRTP.pws",
    "content": "\n[executer]\nRunInTerminal=true\n\n[Project DBase]\nShowLocals=true\n\n[filenumbers]\n0=1846\n1=270\n2=1\n3=34\n4=43\n5=1\n6=4\n7=27\n8=9\n\n[filemarkers]\n0=\n1=\n2=\n3=\n4=\n5=\n6=\n7=\n8=\n\n[File View]\nfilter.file.unmatch=*.so *.o *.a *.la\nfilter.file.ignore.hidden=0\nfilter.dir.ignore.hidden=0\n\n[filelist]\n0=/home/sangamon/src/devel/linphone/oRTP/src/rtpsession.c\n1=/home/sangamon/src/devel/linphone/oRTP/src/rtpsession.h\n2=/home/sangamon/src/devel/linphone/oRTP/src/rtcp.h\n3=/home/sangamon/src/devel/linphone/oRTP/src/scheduler.c\n\n[Project Tree]\n0=0\n1=0:1\n\n[File Tree]\n0=0\n\n[replace_text]\n0=telephone_event\n1=session->rtp\n2=PAYLOAD_AUDIO_CONTINUOUS\n3=ortp_global_stats\n4=session->rtp.rq\n5=session->rtp.wq\n\n[find_text]\n0=rtp_session_create\n1=chunk_item_new\n2=rtcp_calculate_sdes_padding\n3=rtp_session_process\n4=scheduler\n5=rtp\n6=rtp_send\n7=RtpSession\n\n[find_in_files]\n0=exit\n"
  },
  {
    "path": "ortp-config.h.cmake",
    "content": "/***************************************************************************\n* config.h.cmake\n* Copyright (C) 2014  Belledonne Communications, Grenoble France\n*\n****************************************************************************\n*\n* This program is free software; you can redistribute it and/or\n* modify it under the terms of the GNU General Public License\n* as published by the Free Software Foundation; either version 2\n* of the License, or (at your option) any later version.\n*\n* This program is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n* GNU General Public License for more details.\n*\n* You should have received a copy of the GNU General Public License\n* along with this program; if not, write to the Free Software\n* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n*\n****************************************************************************/\n\n#define ORTP_MAJOR_VERSION ${ORTP_MAJOR_VERSION}\n#define ORTP_MINOR_VERSION ${ORTP_MINOR_VERSION}\n#define ORTP_MICRO_VERSION ${ORTP_MICRO_VERSION}\n#define ORTP_VERSION \"${ORTP_VERSION}\"\n\n#define ORTP_LOCAL_RESOURCE_LOCATION \"${CMAKE_CURRENT_SOURCE_DIR}/tester\"\n\n#cmakedefine HAVE_SYS_UIO_H 1\n#cmakedefine HAVE_SYS_AUDIO_H 1\n#cmakedefine HAVE_SYS_SHM_H 1\n#cmakedefine HAVE_ATOMIC 1\n#cmakedefine HAVE_ARC4RANDOM 1\n#cmakedefine HAVE_RECVMSG 1\n#cmakedefine HAVE_SENDMSG 1\n\n#cmakedefine ORTP_BIGENDIAN\n\n#cmakedefine PERF\n#cmakedefine ORTP_TIMESTAMP\n#cmakedefine ORTP_DEBUG_MODE\n#cmakedefine ORTP_DEFAULT_THREAD_STACK_SIZE ${ORTP_DEFAULT_THREAD_STACK_SIZE}\n#cmakedefine POSIXTIMER_INTERVAL ${POSIXTIMER_INTERVAL}\n#cmakedefine __APPLE_USE_RFC_3542\n"
  },
  {
    "path": "ortp.doxygen.in",
    "content": "# Doxyfile 1.8.11\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the config file\n# that follow. The default is UTF-8 which is also the encoding used for all text\n# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv\n# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv\n# for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = oRTP\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         = @ORTP_VERSION@\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = doc\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       =\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = NO\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 8\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:\\n\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". You can put \\n's in the value part of an alias to insert\n# newlines.\n\nALIASES                =\n\n# This tag can be used to specify a number of word-keyword mappings (TCL only).\n# A mapping has the form \"name=value\". For example adding \"class=itcl::class\"\n# will allow you to use the command class in the itcl::class meaning.\n\nTCL_SUBST              =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = YES\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, Javascript,\n# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:\n# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:\n# Fortran. In the later case the parser tries to guess whether the code is fixed\n# or free formatted code, this is the default for Fortran type files), VHDL. For\n# instance to make doxygen treat .inc files as Fortran files (default is PHP),\n# and .f files as C (default is Fortran), use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See http://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = NO\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = NO\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# (class|struct|union) declarations. If set to NO, these declarations will be\n# included in the documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file\n# names in lower-case letters. If set to YES, upper-case letters are also\n# allowed. This is useful if you have classes or files whose names only differ\n# in case and if your file system supports case sensitive file names. Windows\n# and Mac users are advised to set this option to NO.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = YES\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = YES\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = NO\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as not documenting some parameters\n# in a documented function, or documenting parameters that don't exist or using\n# markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong or incomplete\n# parameter documentation, but not about the absence of documentation.\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = @DOXYGEN_INPUT@\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see: http://www.gnu.org/software/libiconv) for the list of\n# possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by doxygen.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,\n# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,\n# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,\n# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd,\n# *.vhdl, *.ucf, *.qsf, *.as and *.js.\n\nFILE_PATTERNS          = *.c \\\n                         *.h \\\n                         *.dox\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = NO\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */test/*\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           = \"@PROJECT_SOURCE_DIR@/src/tests\"\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       =\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# function all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see http://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the config file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = NO\n\n# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in\n# which the alphabetical index list will be split.\n# Minimum value: 1, maximum value: 20, default value: 5.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nCOLS_IN_ALPHA_INDEX    = 5\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list). For an example see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a colorwheel, see\n# http://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use grayscales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to YES can help to show when doxygen was last run and thus if the\n# documentation is up to date.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = NO\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see: http://developer.apple.com/tools/xcode/), introduced with\n# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a\n# Makefile in the HTML output directory. Running make will produce the docset in\n# that directory and running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html\n# for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on\n# Windows.\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the master .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-\n# folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location of Qt's\n# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the\n# generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine-tune the look of the index. As an example, the default style\n# sheet generated by doxygen has an example that shows how to put an image at\n# the root of the tree instead of the PROJECT_NAME. Since the tree basically has\n# the same information as the tab index, you could consider setting\n# DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 1\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# Use the FORMULA_TRANPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# http://www.mathjax.org) which uses client side Javascript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. See the MathJax site (see:\n# http://docs.mathjax.org/en/latest/output.html) for more details.\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility), NativeMML (i.e. MathML) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from http://www.mathjax.org before deployment.\n# The default value is: http://cdn.mathjax.org/mathjax/latest.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = NO\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using Javascript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: http://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: http://xapian.org/). See the section \"External Indexing and\n# Searching\" for details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = YES\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when enabling USE_PDFLATEX this option is only used for generating\n# bitmaps for formulas in the HTML output, but not in the Makefile that is\n# written to the output directory.\n# The default file is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4wide\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the\n# generated LaTeX document. The header should contain everything until the first\n# chapter. If it is left blank doxygen will generate a standard header. See\n# section \"Doxygen usage\" for information on how to let doxygen write the\n# default header to a separate file.\n#\n# Note: Only use a user-defined header if you know what you are doing! The\n# following commands have a special meaning inside the header: $title,\n# $datetime, $date, $doxygenversion, $projectname, $projectnumber,\n# $projectbrief, $projectlogo. Doxygen will replace $title with the empty\n# string, for the replacement values of the other commands the user is referred\n# to HTML_HEADER.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the\n# generated LaTeX document. The footer should contain everything after the last\n# chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer.\n#\n# Note: Only use a user-defined footer if you know what you are doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = NO\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate\n# the PDF file directly from the LaTeX files. Set this option to YES, to get a\n# higher quality PDF documentation.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = NO\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help. This option is also used\n# when generating formulas in HTML.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source\n# code with syntax highlighting in the LaTeX output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_SOURCE_CODE      = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# http://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_TIMESTAMP        = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's config\n# file, i.e. a series of assignments. You only have to provide replacements,\n# missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's config file. A template extensions file can be generated\n# using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code\n# with syntax highlighting in the RTF output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_SOURCE_CODE        = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = YES\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = YES\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the\n# program listings (including syntax highlighting and cross-referencing\n# information) to the DOCBOOK output. Note that enabling this will significantly\n# increase the size of the DOCBOOK output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_PROGRAMLISTING = NO\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see http://autogen.sf.net) file that captures the\n# structure of the code including all documentation. Note that this feature is\n# still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = YES\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           = .\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  = *.h\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             = DOXYGEN \\\n                         MS2_PUBLIC= \\\n                         LINPHONE_PUBLIC= \\\n                         ORTP_PUBLIC\n\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n# The PERL_PATH should be the absolute path and name of the perl script\n# interpreter (i.e. the result of 'which perl').\n# The default file (with absolute path) is: /usr/bin/perl.\n\nPERL_PATH              = /usr/bin/perl\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. Doxygen will then run the mscgen tool (see:\n# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the\n# documentation. The MSCGEN_PATH tag allows you to specify the directory where\n# the mscgen tool resides. If left empty the tool is assumed to be found in the\n# default search path.\n\nMSCGEN_PATH            =\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# When you want a differently looking font in the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = Helvetica\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = YES\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# http://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file. If left blank, it is assumed\n# PlantUML is not used or called during a preprocessing step. Doxygen will\n# generate a warning when it encounters a \\startuml command in this case and\n# will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 1000\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot\n# files that are used to generate the various graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "ortp.pc.in",
    "content": "# This is a comment\nprefix=@prefix@\nexec_prefix=@exec_prefix@\nincludedir=@includedir@\n\nName: oRTP \nDescription: Implement the RFC3550 (RTP) with a easy to use API with high and low level access.\nVersion: @ORTP_PKGCONFIG_VERSION@\nLibs: -L@libdir@ -lortp @ORTPDEPS_LIBS@\nCflags: -I@includedir@ @ORTPDEPS_CFLAGS@\n"
  },
  {
    "path": "ortp.spec.in",
    "content": "# -*- rpm-spec -*-\n#\n# ortp -- Real-time Transport Protocol Stack\n#\n# Default is optimized for Pentium IV but will execute on Pentium II &\n# later (i686).\n\n# These 2 lines are here because we can build the RPM for flexisip, in which \n# case we prefix the entire installation so that we don't break compatibility\n# with the user's libs.\n# To compile with bc prefix, use rpmbuild -ba --with bc [SPEC]\n%define \t\tpkg_name \t%{?_with_bc:bc-ortp}%{!?_with_bc:ortp}\n%{?_with_bc: %define \t_prefix\t\t/opt/belledonne-communications}\n%define \t\tsrtp \t\t%{?_without_srtp:0}%{?!_without_srtp:1}\n\n# re-define some directories for older RPMBuild versions which don't. This messes up the doc/ dir\n# taken from https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros\n%define _datarootdir       %{_prefix}/share\n%define _datadir           %{_datarootdir}\n%define _docdir            %{_datadir}/doc\n\n%ifarch %ix86\n%define\t\tortp_cpu\tpentium4\n%endif\n\nSummary:\tReal-time Transport Protocol Stack\nName:\t\t%pkg_name\nVersion:\t@ORTP_PKGCONFIG_VERSION@\nRelease:\t%(git describe --tags --abbrev=40 | sed -rn 's/^.*-([0-9]+)-g[a-z0-9]{40}$/\\1/p' || echo '1')%{?dist}\n#to be alined with redhat which changed epoc to 1 for an unknown reason\nEpoch:\t\t1\nLicense:\tLGPL\nGroup:\t\tApplications/Communications\nURL:\t\thttp://linphone.org/ortp/\nSource0:\t%{name}-%{version}.tar.gz\nBuildRoot:\t%{_tmppath}/%{name}-%{version}-%{release}-buildroot\n%ifarch %ix86\nBuildArch:\ti686\n%endif\n\n%description\noRTP is a LGPL licensed C library implementing the RTP protocol\n(rfc3550). It is available for most unix clones (primilarly Linux and\nHP-UX), and Microsoft Windows.\n\n%package        devel\nSummary:        Headers, libraries and docs for the oRTP library\nGroup:          Development/Libraries\nBuildRequires:\tdoxygen\n#to be alined with redhat which changed epoc to 1 for an unknown reason\nEpoch:\t\t1\nRequires:      %{name} = %{epoch}:%{version}-%{release}\n\n%description    devel\noRTP is a LGPL licensed C library implementing the RTP protocol\n(rfc1889). It is available for most unix clones (primilarly Linux and\nHP-UX), and Microsoft Windows.\n\nThis package contains header files and development libraries needed to\ndevelop programs using the oRTP library.\n\n%ifarch %ix86\n%define\tortp_arch_cflags -malign-double -march=i686 -mtune=%{ortp_cpu}\n%else\n# Must be non-empty\n%define ortp_arch_cflags -Wall\n%endif\n%define ortp_cflags %ortp_arch_cflags -Wall -g -pipe -pthread -O3 -fomit-frame-pointer -fno-schedule-insns -fschedule-insns2 -fno-strict-aliasing\n\n%prep\n%setup\n\n%build\n%configure \\\n\t--enable-shared \\\n\t--enable-static \\\n%if !%{srtp}\n\t--with-srtp=none \\\n%endif\n\t--docdir=%{_docdir}\n\n%{__make} -j$RPM_BUILD_NCPUS CFLAGS=\"%ortp_cflags\" CXXFLAGS=\"%ortp_cflags\"\n\n%install\nrm -rf $RPM_BUILD_ROOT\nmake install DESTDIR=$RPM_BUILD_ROOT\n\n%clean\nrm -rf $RPM_BUILD_ROOT\n\n%files\n%defattr(-,root,root,-)\n%doc %{_docdir}/ortp-%{version}/README\n%doc %{_docdir}/ortp-%{version}/ChangeLog\n%doc %{_docdir}/ortp-%{version}/COPYING\n%doc %{_docdir}/ortp-%{version}/AUTHORS\n%{_libdir}/*.so.*\n\n%files devel\n%defattr(-,root,root,-)\n%doc %{_docdir}/ortp-%{version}/html/*\n%{_libdir}/*.la\n%{_libdir}/*.a\n%{_libdir}/*.so\n%{_libdir}/pkgconfig/*.pc\n%{_includedir}/*\n\n%changelog\n* Tue Oct 25 2005 Francois-Xavier Kowalski <fix@hp.com>\n- Add to oRTP distribution with \"make rpm\" target\n"
  },
  {
    "path": "pkg.list",
    "content": "# -*- rpm-spec -*- ############################################################\n# \n# EPM list file.  See epm(1) and epm.list(5) for details\n# \n###############################################################################\n\n%product\t${SUMMARY}\n%version\t${VERSION}\n%release\t${RELEASE}\n%description  \t${SUMMARY}\n%vendor\t\t${VENDOR}\n%copyright\t${LICENSE}\n%license\t${LICENSE}\n%readme\t\t${srcdir}/README\n%packager\t${PACKAGER}\n\n%system linux\n\n# Package all-in one: should be split later on...\n%provides ortp-devel\n\n%postinstall << EOF\nldconfig 2>&1 | logger -i -s -t ${PACKAGE}\nEOF\n\n%postremove << EOF\nldconfig 2>&1 | logger -i -s -t ${PACKAGE}\nEOF\n\n%system hpux\n\n%system all\n\n%include files.list\n\n%provides ortp\n%replaces ortp\n\n"
  },
  {
    "path": "src/.gitignore",
    "content": "*.lo\n.deps\n.libs\nMakefile\nMakefile.in\nlibortp.la\nmrtprecv\nmrtpsend\nrtpmemtest\nrtprecv\nrtpsend\ntest_timer\ntevmrtprecv\ntevrtprecv\ntevrtpsend\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "############################################################################\n# Copyright (c) 2010-2023 Belledonne Communications SARL.\n#\n# This file is part of oRTP \n# (see https://gitlab.linphone.org/BC/public/ortp).\n#\n############################################################################\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as\n# published by the Free Software Foundation, either version 3 of the\n# License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. If not, see <http://www.gnu.org/licenses/>.\n############################################################################\n\nset(LIBS )\nif(NOT ANDROID)\n\tlist(APPEND LIBS ${CMAKE_THREAD_LIBS_INIT})\nendif()\nif(HAVE_RT)\n\tlist(APPEND LIBS rt)\nendif()\nif(LIBM)\n\tlist(APPEND LIBS ${LIBM})\nendif()\n\nset(ORTP_SOURCE_FILES_C\n\taudiobandwidthestimator.c\n\tavprofile.c\n\tcongestiondetector.c\n\tevent.c\n\textremum.c\n\tjitterctl.c\n\tkalmanrls.c\n\tlogging.c\n\tnack.c\n\tnetsim.c\n\tortp.c\n\tpayloadtype.c\n\tport.c\n\tposixtimer.c\n\trtcp.c\n\trtcp_fb.c\n\trtcp_xr.c\n\trtcpparse.c\n\trtpaudiolevel.c\n\trtpframemarking.c\n\trtpparse.c\n\trtpprofile.c\n\trtpsession.c\n\trtpsession_inet.c\n\trtpsignaltable.c\n\trtptimer.c\n\tscheduler.c\n\tsessionset.c\n\tstr_utils.c\n\ttelephonyevents.c\n\tutils.c\n)\nset(ORTP_SOURCE_FILES_CXX\n\tdblk.cc\t#HAVE_ATOMIC is mandatory\n\trtpbundle.cc\n\tvideobandwidthestimator.cc\n\tbandwidth-measurer.cc\n\tfecstream/fecstream.cc\n\tfecstream/fec-stream-stats.cc\n\tfecstream/fec-encoder.cpp\n\tfecstream/packet-api.cpp\n\tfecstream/receive-cluster.cpp\n\tfecstream/fec-packets-connection.cpp\n\tfecstream/fec-params.cpp\n\tfecstream/overhead.cpp\n)\n\nadd_definitions(-DBCTBX_LOG_DOMAIN=\"ortp\")\n\nif(WIN32)\n\tlist(APPEND ORTP_SOURCE_FILES_C dll_entry.c)\n\tif(CMAKE_SYSTEM_NAME STREQUAL \"WindowsPhone\")\n\t\tlist(APPEND ORTP_SOURCE_FILES_CXX winrttimer.cpp winrttimer.h)\n\t\tset_source_files_properties(winrttimer.cpp PROPERTIES COMPILE_FLAGS \"/ZW /AI\\$(WindowsSDK_MetadataPath)\")\n\tendif()\n\tlist(APPEND LIBS ws2_32)# symbols for in6addr_any\nendif()\n\nif(WIN32)\n\tadd_definitions(-DWINDOWS_NATIVE)\n\tif(NOT CMAKE_SYSTEM_NAME STREQUAL \"WindowsStore\")\n\t\tlist(APPEND LIBS ws2_32)\n\tendif()\n\tif(NOT CMAKE_SYSTEM_NAME STREQUAL \"WindowsPhone\" AND NOT CMAKE_SYSTEM_NAME STREQUAL \"WindowsStore\")\n\t\tlist(APPEND LIBS delayimp Winmm Qwave)\n\tendif()\nendif()\n\nbc_apply_compile_flags(ORTP_SOURCE_FILES_C STRICT_OPTIONS_CPP STRICT_OPTIONS_C)\nbc_apply_compile_flags(ORTP_SOURCE_FILES_CXX STRICT_OPTIONS_CPP STRICT_OPTIONS_CXX)\n\n\nadd_library(ortp ${ORTP_HEADER_FILES} ${ORTP_SOURCE_FILES_C} ${ORTP_SOURCE_FILES_CXX})\nset_target_properties(ortp PROPERTIES LINKER_LANGUAGE CXX)\nset_target_properties(ortp PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON)\ntarget_include_directories(ortp\n\tPUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>\n\t$<INSTALL_INTERFACE:include>\n)\ntarget_link_libraries(ortp PUBLIC ${BCToolbox_TARGET} PRIVATE ${LIBS})\nif(WIN32)\n\ttarget_compile_options(ortp PRIVATE \"/DELAYLOAD:Qwave.dll\")\nendif()\n\nif(BUILD_SHARED_LIBS)\n\ttarget_compile_definitions(ortp PRIVATE \"ORTP_EXPORTS\")\n\tif(APPLE)\n\t\tset_target_properties(ortp PROPERTIES\n\t\t\tFRAMEWORK TRUE\n\t\t\tMACOSX_FRAMEWORK_IDENTIFIER org.linphone.ortp\n  \t\t\tMACOSX_FRAMEWORK_INFO_PLIST \"${PROJECT_SOURCE_DIR}/build/osx/Info.plist.in\"\n\t\t\tPUBLIC_HEADER \"${ORTP_HEADER_FILES}\"\n\t\t)\n\tendif()\n\tif(NOT ANDROID)\n\t\t# Do not version shared library on Android\n\t\tset_target_properties(ortp PROPERTIES SOVERSION ${ORTP_SO_VERSION})\n\tendif()\n\tif(MSVC)\n\t\tinstall(FILES $<TARGET_PDB_FILE:ortp>\n\t\t\tDESTINATION ${CMAKE_INSTALL_BINDIR}\n\t\t\tPERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE\n\t\t\tCONFIGURATIONS Debug RelWithDebInfo\n\t\t)\n\tendif()\nelse()\n\ttarget_compile_definitions(ortp PUBLIC \"ORTP_STATIC\")\nendif()\n\ninstall(TARGETS ortp EXPORT ${PROJECT_NAME}Targets\n\tRUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}\n\tLIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\n\tARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}\n\tFRAMEWORK DESTINATION Frameworks\n\tPERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE\n)\n\nif(ENABLE_TESTS AND NOT CMAKE_SYSTEM_NAME STREQUAL \"WindowsPhone\" AND NOT CMAKE_SYSTEM_NAME STREQUAL \"WindowsStore\")\n\tadd_subdirectory(tests)\nendif()\n"
  },
  {
    "path": "src/Makefile.am",
    "content": "\nEXTRA_DIST=dll_entry.c winrttimer.cpp winrttimer.h\n\nAM_CPPFLAGS=\\\n\t$(STRICT_OPTIONS) \\\n\t$(STRICT_OPTIONS_CXX) \\\n\t-I$(top_srcdir)/include/ \\\n\t-I$(top_srcdir) \\\n\t$(BCTOOLBOX_CFLAGS)\n\nAM_CFLAGS=\\\n\t$(STRICT_OPTIONS) \\\n\t$(STRICT_OPTIONS_CC) \\\n\t$(PTHREAD_CFLAGS) \\\n\t$(TRUESPEECH_CFLAGS)\n\nAM_LDFLAGS=$(PTHREAD_LDFLAGS)\n\nlib_LTLIBRARIES = libortp.la\n\nlibortp_la_SOURCES=\t\\\n\t\t\tavprofile.c  \\\n\t\t\tb64.c \\\n\t\t\tcongestiondetector.c congestiondetector.h\\\n\t\t\tevent.c \\\n\t\t\textremum.c \\\n\t\t\tkalmanrls.c \\\n\t\t\tjitterctl.c jitterctl.h \\\n\t\t\tlogging.c \\\n\t\t\tnack.c \\\n\t\t\tnetsim.c \\\n\t\t\tortp.c \\\n\t\t\tpayloadtype.c \\\n\t\t\tport.c \\\n\t\t\tposixtimer.c \\\n\t\t\trtcp.c \\\n\t\t\trtcp_fb.c \\\n\t\t\trtcp_xr.c \\\n\t\t\trtcpparse.c \\\n\t\t\trtpparse.c  \\\n\t\t\trtpprofile.c \\\n\t\t\trtpsession.c \\\n\t\t\trtpsession_inet.c \\\n\t\t\trtpsession_priv.h \\\n\t\t\trtpsignaltable.c  \\\n\t\t\trtptimer.c\trtptimer.h \\\n\t\t\tscheduler.c scheduler.h \\\n\t\t\tsessionset.c  \\\n\t\t\tstr_utils.c \t\\\n\t\t\ttelephonyevents.c  \\\n\t\t\tvideobandwidthestimator.c videobandwidthestimator.h\\\n\t\t\tutils.c utils.h\n\nlibortp_la_LIBADD= $(PTHREAD_LIBS) $(RT_LIBS) -lm $(BCTOOLBOX_LIBS)\n\nlibortp_la_LDFLAGS= -version-info $(LIBORTP_SO_VERSION) -no-undefined\n\nif HAVE_LD_OUTPUT_DEF\nlibortp_la_LDFLAGS += -Wl,--output-def,libortp-$(LIBORTP_SO_CURRENT).def\ndefexecdir = $(libdir)\ndefexec_DATA = libortp-$(LIBORTP_SO_CURRENT).def\nCLEANFILES = $(defexec_DATA)\n\nlibortp-$(LIBORTP_SO_CURRENT).def: libortp.la\n\nif BUILD_WIN32\ndefexec_DATA += libortp-$(LIBORTP_SO_CURRENT).lib\nlibortp-$(LIBORTP_SO_CURRENT).lib: libortp-$(LIBORTP_SO_CURRENT).def libortp.la\n\t$(DLLTOOL) --dllname libortp-$(LIBORTP_SO_CURRENT).dll --input-def libortp-$(LIBORTP_SO_CURRENT).def --output-lib $@ libortp.la\nendif\nendif\n\nSUBDIRS= . tests\n"
  },
  {
    "path": "src/audiobandwidthestimator.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"audiobandwidthestimator.h\"\n#include \"congestiondetector.h\"\n#include \"rtpsession_priv.h\"\n#include <math.h>\n#include <ortp/logging.h>\n#include <ortp/rtpsession.h>\n\n// 2 micro seconds diff, we do not really need to be precise, so it is ok to use a very low\n// minimal period, it will just detect that the BW is very high\n#define MIN_DIFFTIME 0.000002f\n\nstatic void compute_bitrate_add_to_list_and_remove_oldest_value(OrtpAudioBandwidthEstimator *abe,\n                                                                OrtpAudioBandwidthEstimatorPacket *packet) {\n\tfloat difftime = (float)(packet->recv_last_timestamp.tv_sec - packet->recv_first_timestamp.tv_sec) +\n\t                 1e-6f * (packet->recv_last_timestamp.tv_usec - packet->recv_first_timestamp.tv_usec);\n\n\tif (difftime >= MIN_DIFFTIME) {\n\t\tpacket->bitrate = (packet->bytes * 8 / difftime);\n\t\tortp_debug(\"[ABE] Bitrate is %f kbits/s computed using %f timedif and %u size\", packet->bitrate / 1000,\n\t\t           difftime, packet->bytes);\n\n\t\tabe->nb_packets_computed += 1;\n\t\tabe->packets = bctbx_list_prepend(abe->packets, packet);\n\n\t\tif (bctbx_list_size(abe->packets) > abe->packets_history_size) {\n\t\t\tvoid *old_data = bctbx_list_nth_data(abe->packets, abe->packets_history_size);\n\t\t\tabe->packets = bctbx_list_remove(abe->packets, old_data);\n\t\t\tortp_free(old_data);\n\t\t}\n\n\t\tif (abe->nb_packets_computed % abe->packets_history_size == 0) {\n\t\t\tOrtpEvent *ev = ortp_event_new(ORTP_EVENT_NEW_AUDIO_BANDWIDTH_ESTIMATION_AVAILABLE);\n\t\t\tOrtpEventData *ed = ortp_event_get_data(ev);\n\t\t\ted->info.audio_bandwidth_available = ortp_audio_bandwidth_estimator_get_estimated_available_bandwidth(abe);\n\t\t\tortp_message(\n\t\t\t    \"[ABE] Dispatching event ORTP_EVENT_NEW_AUDIO_BANDWIDTH_ESTIMATION_AVAILABLE with value %f kbits/s\",\n\t\t\t    ed->info.audio_bandwidth_available / 1000);\n\t\t\trtp_session_dispatch_event(abe->session, ev);\n\t\t}\n\t} else {\n\t\tortp_free(packet);\n\t}\n}\n\n/**\n * Check if a packet is a duplicate we should use for bandwidth estimation\n * Duplicated packets are expected to arrive one just after another, so this\n * is just storing the last sent_timestamp and seq_number. If they match, packet is considered\n * a duplicate.\n *\n * Random duplicates created by the network may not arrive one after an other and\n * may not be detected by this simple algorithm, they are removed by rtp_putq. They may interfer\n * with this mechanism, so one should not push too high the trust percentage\n *\n * If the packet is a duplicate, create an ABE packet and add it to the history, this may trigger\n * an event with a bandwidth estimation\n *\n * @param [in]\tabe\t\tthe audio bandwidth estimator engine\n * @param[in]\tsent_timestamp\tRTP packet sent timestamp\n * @param[in]\trecv_timestamp\tRTP packet recv timestamp\n * @param[in]\tmsgsize\t\tRTP packet size(including IP overhead)\n * @param[in]\tseq_number\tRTP packet seq_number\n *\n * @return TRUE if the RTP packet is detected as a duplicate\n */\nstatic bool_t ortp_audio_bandwidth_estimator_process_packet(OrtpAudioBandwidthEstimator *abe,\n                                                            uint32_t sent_timestamp,\n                                                            const struct timeval *recv_timestamp,\n                                                            int msgsize,\n                                                            uint16_t seq_number) {\n\n\t/* bw estimator detects duplicates sent on purpose by the other side\n\t * duplicates are sent simultaneously, measure the recv timestamp difference\n\t * to estimate the actuel bw */\n\tif (abe->last_seq_number == seq_number && abe->last_sent_timestamp == sent_timestamp) {\n\t\t/* When a congestion is currently on, do not estimate the bandwidth, it shall\n\t\t * be increased via TMMBR anyway at the congestion end and we do not want to\n\t\t * get some measurement from congested period as they could then be selected\n\t\t * when we have enough measurement but after the end of it */\n\t\tif (!(abe->session->congestion_detector_enabled && abe->session->rtp.congdetect &&\n\t\t      abe->session->rtp.congdetect->is_in_congestion)) {\n\t\t\tOrtpAudioBandwidthEstimatorPacket *abe_packet =\n\t\t\t    (OrtpAudioBandwidthEstimatorPacket *)ortp_malloc0(sizeof(OrtpAudioBandwidthEstimatorPacket));\n\t\t\tabe->stats.recv_dup++;\n\t\t\tabe_packet->bytes = msgsize; // Should the second packet size be considered or just the first one?\n\t\t\t                             // Considering only one seems to give better results\n\t\t\tabe_packet->recv_first_timestamp.tv_sec = abe->last_timestamp.tv_sec;\n\t\t\tabe_packet->recv_first_timestamp.tv_usec = abe->last_timestamp.tv_usec;\n\t\t\tabe_packet->recv_last_timestamp.tv_sec = recv_timestamp->tv_sec;\n\t\t\tabe_packet->recv_last_timestamp.tv_usec = recv_timestamp->tv_usec;\n\t\t\tcompute_bitrate_add_to_list_and_remove_oldest_value(abe, abe_packet);\n\t\t}\n\t\treturn TRUE;\n\t} else {\n\t\tabe->last_sent_timestamp = sent_timestamp;\n\t\tabe->last_timestamp.tv_sec = recv_timestamp->tv_sec;\n\t\tabe->last_timestamp.tv_usec = recv_timestamp->tv_usec;\n\t\tabe->last_seq_number = seq_number;\n\t\treturn FALSE;\n\t}\n}\n\n/**** Modifier functions: the ABE use the highest priority modifier to inject/retrieve\n * duplicated RTP packet\n */\nstatic int ortp_abe_process_onsend(struct _RtpTransportModifier *tm, mblk_t *packet) {\n\trtp_header_t *rtp = (rtp_header_t *)packet->b_rptr;\n\tsize_t msgsize = msgdsize(packet);\n\t/* ignore anything not RTP */\n\tif (rtp->version == 2) {\n\t\tOrtpAudioBandwidthEstimator *abe = (OrtpAudioBandwidthEstimator *)tm->data;\n\t\tRtpSession *session = abe->session;\n\t\t// Send a duplicate if: the target bandwidth is not the max requested\n\t\tif ((session->audio_bandwidth_estimator_enabled &&\n\t\t     session->target_upload_bandwidth < session->max_target_upload_bandwidth)) {\n\t\t\tif (abe->next_duplicated == 0) {\n\t\t\t\tRtpTransport *rtpt = NULL;\n\t\t\t\trtp_session_get_transports(session, &rtpt, NULL);\n\t\t\t\tmeta_rtp_transport_modifier_inject_packet_to_send(rtpt, tm, packet, 0);\n\t\t\t\tortp_debug(\"[ABE] duplicate outgoing packet on session [%p]\", session);\n\t\t\t\tabe->next_duplicated = abe->duplicated_packet_rate;\n\t\t\t\tabe->stats.sent_dup++;\n\t\t\t} else {\n\t\t\t\tabe->next_duplicated--;\n\t\t\t}\n\t\t}\n\t}\n\treturn (int)msgsize;\n}\nstatic int ortp_abe_process_onreceive(struct _RtpTransportModifier *tm, mblk_t *packet) {\n\trtp_header_t *rtp = (rtp_header_t *)packet->b_rptr;\n\tsize_t msgsize = msgdsize(packet);\n\t/* ignore anything not RTP */\n\tif (rtp->version == 2) {\n\t\tOrtpAudioBandwidthEstimator *abe = (OrtpAudioBandwidthEstimator *)tm->data;\n\t\tRtpSession *session = abe->session;\n\t\tint overhead = ortp_stream_is_ipv6(&session->rtp.gs) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;\n\t\tif (ortp_audio_bandwidth_estimator_process_packet(abe, rtp_header_get_timestamp(rtp), &packet->timestamp,\n\t\t                                                  (int)(msgsize + overhead), rtp_header_get_seqnumber(rtp))) {\n\t\t\treturn 0; // packet is a duplicate, drop it\n\t\t}\n\t}\n\treturn (int)msgsize;\n}\nstatic void ortp_abe_modifier_destroy(RtpTransportModifier *tm) {\n\tortp_free(tm);\n}\n\nOrtpAudioBandwidthEstimator *ortp_audio_bandwidth_estimator_new(RtpSession *session) {\n\tRtpTransport *transport = NULL;\n\t/* init ABE parameters */\n\tOrtpAudioBandwidthEstimator *abe = (OrtpAudioBandwidthEstimator *)ortp_malloc0(sizeof(OrtpAudioBandwidthEstimator));\n\tabe->session = session;\n\tabe->packets_history_size = 10;\n\tabe->trust_percentage = 65;\n\tabe->nb_packets_computed = 0;\n\tabe->packets = NULL;\n\tabe->duplicated_packet_rate = 10;\n\tabe->next_duplicated = 0;\n\tabe->last_timestamp.tv_sec = 0;\n\tabe->last_timestamp.tv_usec = 0;\n\tabe->last_seq_number = 0;\n\tabe->stats.sent_dup = 0;\n\tabe->stats.recv_dup = 0;\n\n\t/* Create transport modifiers to be able to duplicate outgoing packets\\\n\t * duplicated outgoing packets are sent simultaneously and used on the receiving side\n\t * to measure the available bw\n\t */\n\trtp_session_get_transports(session, &transport, NULL);\n\tabe->modifier = ortp_new0(RtpTransportModifier, 1);\n\tabe->modifier->level = RtpTransportModifierLevelAudioBandwidthEstimator; // Use higher level, so this modifier is\n\t                                                                         // processed even after the FEC one\n\tabe->modifier->data = abe;\n\tabe->modifier->t_process_on_send = ortp_abe_process_onsend;\n\tabe->modifier->t_process_on_receive = ortp_abe_process_onreceive;\n\tabe->modifier->t_process_on_schedule = NULL;\n\tabe->modifier->t_destroy = ortp_abe_modifier_destroy;\n\tmeta_rtp_transport_append_modifier(transport, abe->modifier);\n\n\treturn abe;\n}\n\nvoid ortp_audio_bandwidth_estimator_destroy(OrtpAudioBandwidthEstimator *abe) {\n\tortp_audio_bandwidth_estimator_reset(abe);\n\tortp_free(abe);\n}\n\nvoid ortp_audio_bandwidth_estimator_reset(OrtpAudioBandwidthEstimator *abe) {\n\tabe->nb_packets_computed = 0;\n\tabe->last_timestamp.tv_sec = 0;\n\tabe->last_timestamp.tv_usec = 0;\n\tabe->last_seq_number = 0;\n\tabe->packets = bctbx_list_free_with_data(abe->packets, bctbx_free);\n}\n\nvoid ortp_audio_bandwidth_estimator_set_history_max_size(OrtpAudioBandwidthEstimator *abe, unsigned int value) {\n\tabe->packets_history_size = value;\n}\n\nvoid ortp_audio_bandwidth_estimator_set_trust(OrtpAudioBandwidthEstimator *abe, unsigned int value) {\n\tabe->trust_percentage = value;\n}\nvoid ortp_audio_bandwidth_estimator_set_duplicate_rate(OrtpAudioBandwidthEstimator *abe, unsigned int value) {\n\tabe->duplicated_packet_rate = value;\n}\n\nunsigned int ortp_audio_bandwidth_estimator_get_history_max_size(OrtpAudioBandwidthEstimator *abe) {\n\treturn abe->packets_history_size;\n}\n\nunsigned int ortp_audio_bandwidth_estimator_get_trust(OrtpAudioBandwidthEstimator *abe) {\n\treturn abe->trust_percentage;\n}\nunsigned int ortp_audio_bandwidth_estimator_get_duplicate_rate(OrtpAudioBandwidthEstimator *abe) {\n\treturn abe->duplicated_packet_rate;\n}\n\nstatic int compare_float(const float *v1, const float *v2) {\n\tif (*v1 == *v2) return 0;\n\tif (*v1 < *v2) return 1;\n\treturn -1;\n}\n\nfloat ortp_audio_bandwidth_estimator_get_estimated_available_bandwidth(OrtpAudioBandwidthEstimator *abe) {\n\tbctbx_list_t *bitrate_sorted = NULL;\n\tbctbx_list_t *elem;\n\tfloat *result = NULL;\n\tint index = (int)(abe->trust_percentage * abe->packets_history_size / 100);\n\tfor (elem = abe->packets; elem != NULL; elem = bctbx_list_next(elem)) {\n\t\tOrtpAudioBandwidthEstimatorPacket *packet = (OrtpAudioBandwidthEstimatorPacket *)bctbx_list_get_data(elem);\n\t\tbitrate_sorted = bctbx_list_insert_sorted(bitrate_sorted, &packet->bitrate, (bctbx_compare_func)compare_float);\n\t}\n\tresult = (float *)bctbx_list_nth_data(bitrate_sorted, index);\n\tbctbx_list_free(bitrate_sorted);\n\treturn (float)*result;\n}\n"
  },
  {
    "path": "src/audiobandwidthestimator.h",
    "content": "/*\n * Copyright (c) 2010-2023 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef AUDIOBANDWIDTHESTIMATOR_H\n#define AUDIOBANDWIDTHESTIMATOR_H\n\n#include <bctoolbox/list.h>\n#include <ortp/port.h>\n#include <ortp/rtpsession.h>\n#include <ortp/utils.h>\n\ntypedef struct _OrtpAudioBandwidthEstimatorPacket {\n\tstruct timeval recv_first_timestamp;\n\tstruct timeval recv_last_timestamp;\n\tunsigned int bytes;\n\tfloat bitrate;\n} OrtpAudioBandwidthEstimatorPacket;\n\ntypedef struct _OrtpAudioBandwidthEstimator {\n\t/** Session access */\n\tstruct _RtpSession *session;\n\tRtpTransportModifier *modifier;\n\t/** Settings */\n\tunsigned int packets_history_size; /**< ABE packets history size: when full, generate an estimation */\n\tunsigned int trust_percentage;\n\tunsigned int duplicated_packet_rate; /**< 1 out duplicated_packet_rate is duplicated */\n\n\t/** info on last processed RPT packet */\n\tstruct timeval last_timestamp;\n\tuint16_t last_seq_number;\n\tuint32_t last_sent_timestamp;\n\n\tbctbx_list_t *packets;        /**< ABE packet history */\n\tint nb_packets_computed;      /**< ABE packet history size */\n\tunsigned int next_duplicated; /**< countdown to enforce the duplicated_packet_rate */\n\tabe_stats_t stats;            /**< statistics */\n} OrtpAudioBandwidthEstimator;\n\nOrtpAudioBandwidthEstimator *ortp_audio_bandwidth_estimator_new(struct _RtpSession *session);\n\nvoid ortp_audio_bandwidth_estimator_destroy(OrtpAudioBandwidthEstimator *abe);\n\nvoid ortp_audio_bandwidth_estimator_reset(OrtpAudioBandwidthEstimator *abe);\n\n/**\n * Sets the number of rate duplicates packets are generated.\n * rate is applied as follow, with a rate of 10, every 10 RTP packets sent, one is duplicated\n * Packets are generated only when the current target_upload_bandwidth on RtpSession\n * is inferior to media_stream_set_max_network_bitrate\n * Default value is 10.\n */\nvoid ortp_audio_bandwidth_estimator_set_duplicate_rate(OrtpAudioBandwidthEstimator *abe, unsigned int value);\n\n/**\n * Sets the number of duplicates packets needed to compute the available audio bandwidth.\n * Using a duplicate rate of 10 one packet out of 10 is duplicated, if ptime is 100ms,\n * one duplicate is sent every second so we will collect 10 duplicates packets every 10s\n * to produce the estimate.\n * Default value is 10.\n */\nvoid ortp_audio_bandwidth_estimator_set_history_max_size(OrtpAudioBandwidthEstimator *abe, unsigned int value);\n\n/**\n * Sets the percentage for which the chosen bandwidth value in all available will be inferior.\n * Example: for 100 packets with 90% trust, bandwidth value will be the 90th after sorted.\n * Default value is 65.\n */\nvoid ortp_audio_bandwidth_estimator_set_trust(OrtpAudioBandwidthEstimator *abe, unsigned int value);\n\n/**\n * Gets the number of packets needed to compute the available audio bandwidth.\n * Default value is 10.\n */\nunsigned int ortp_audio_bandwidth_estimator_get_history_max_size(OrtpAudioBandwidthEstimator *abe);\n\n/**\n * Gets the percentage for which the chosen bandwidth value in all available will be inferior.\n * Example: for 100 packets with 90% trust, bandwidth value will be the 90th after sorted.\n * Default value is 65.\n */\nunsigned int ortp_audio_bandwidth_estimator_get_trust(OrtpAudioBandwidthEstimator *abe);\n\n/**\n * Gets the number of rate duplicates packets are generated.\n * rate is applied as follow, with a rate of 10, every 10 RTP packets sent, one is duplicated\n * Packets are generated only when the current target_upload_bandwidth on RtpSession\n * is inferior to media_stream_set_max_network_bitrate\n * Default value is 10.\n */\nunsigned int ortp_audio_bandwidth_estimator_get_duplicate_rate(OrtpAudioBandwidthEstimator *abe);\n\nfloat ortp_audio_bandwidth_estimator_get_estimated_available_bandwidth(OrtpAudioBandwidthEstimator *abe);\n\n#endif\n"
  },
  {
    "path": "src/avprofile.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <ortp/ortp.h>\n#include <ortp/payloadtype.h>\n#include <ortp/rtcp.h>\n#include <ortp/rtpprofile.h>\n\nchar offset127 = 127;\nchar offset0xD5 = (char)0xD5;\nchar offset0[4] = {0x00, 0x00, 0x00, 0x00};\n\n/*\n * IMPORTANT : some compiler don't support the tagged-field syntax. Those\n * macros are there to trap the problem This means that if you want to keep\n * portability, payload types must be defined with their fields in the right\n * order.\n */\n#if defined(_ISOC99_SOURCE) || defined(__clang__)\n// ISO C99's tagged syntax\n#define TYPE(val) .type = (val)\n#define CLOCK_RATE(val) .clock_rate = (val)\n#define BITS_PER_SAMPLE(val) .bits_per_sample = (val)\n#define ZERO_PATTERN(val) .zero_pattern = (val)\n#define PATTERN_LENGTH(val) .pattern_length = (val)\n#define NORMAL_BITRATE(val) .normal_bitrate = (val)\n#define MIME_TYPE(val) .mime_type = (val)\n#define CHANNELS(val) .channels = (val)\n#define RECV_FMTP(val) .recv_fmtp = (val)\n#define SEND_FMTP(val) .send_fmtp = (val)\n#define NO_AVPF .avpf = {.features = PAYLOAD_TYPE_AVPF_NONE, .trr_interval = 0}\n#define AVPF(feat, intv) .avpf = {.features = (feat), .trr_interval = (intv)}\n#define FLAGS(val) .flags = (val)\n#elif defined(__GNUC__)\n// GCC's legacy tagged syntax (even old versions have it)\n#define TYPE(val)                                                                                                      \\\n\ttype:                                                                                                              \\\n\t(val)\n#define CLOCK_RATE(val)                                                                                                \\\n\tclock_rate:                                                                                                        \\\n\t(val)\n#define BITS_PER_SAMPLE(val)                                                                                           \\\n\tbits_per_sample:                                                                                                   \\\n\t(val)\n#define ZERO_PATTERN(val)                                                                                              \\\n\tzero_pattern:                                                                                                      \\\n\t(val)\n#define PATTERN_LENGTH(val)                                                                                            \\\n\tpattern_length:                                                                                                    \\\n\t(val)\n#define NORMAL_BITRATE(val)                                                                                            \\\n\tnormal_bitrate:                                                                                                    \\\n\t(val)\n#define MIME_TYPE(val)                                                                                                 \\\n\tmime_type:                                                                                                         \\\n\t(val)\n#define CHANNELS(val)                                                                                                  \\\n\tchannels:                                                                                                          \\\n\t(val)\n#define RECV_FMTP(val)                                                                                                 \\\n\trecv_fmtp:                                                                                                         \\\n\t(val)\n#define SEND_FMTP(val)                                                                                                 \\\n\tsend_fmtp:                                                                                                         \\\n\t(val)\n#define NO_AVPF                                                                                                        \\\n\tavpf : {                                                                                                           \\\n\tfeatures:                                                                                                          \\\n\t\tPAYLOAD_TYPE_AVPF_NONE, trr_interval : 0                                                                       \\\n\t}\n#define AVPF(feat, intv)                                                                                               \\\n\tavpf : {                                                                                                           \\\n\tfeatures:                                                                                                          \\\n\t\t(feat), trr_interval : (intv)                                                                                  \\\n\t}\n#define FLAGS(val)                                                                                                     \\\n\tflags:                                                                                                             \\\n\t(val)\n#else\n// No tagged syntax supported\n#define TYPE(val) (val)\n#define CLOCK_RATE(val) (val)\n#define BITS_PER_SAMPLE(val) (val)\n#define ZERO_PATTERN(val) (val)\n#define PATTERN_LENGTH(val) (val)\n#define NORMAL_BITRATE(val) (val)\n#define MIME_TYPE(val) (val)\n#define CHANNELS(val) (val)\n#define RECV_FMTP(val) (val)\n#define SEND_FMTP(val) (val)\n#define NO_AVPF                                                                                                        \\\n\t{ PAYLOAD_TYPE_AVPF_NONE, 0 }\n#define AVPF(feat, intv)                                                                                               \\\n\t{ (feat), FALSE, (intv) }\n#define FLAGS(val) (val)\n\n#endif\n\nPayloadType payload_type_pcmu8000 = {\n    TYPE(PAYLOAD_AUDIO_CONTINUOUS),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(8),\n    ZERO_PATTERN(&offset127),\n    PATTERN_LENGTH(1),\n    NORMAL_BITRATE(64000),\n    MIME_TYPE(\"PCMU\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_pcma8000 = {\n    TYPE(PAYLOAD_AUDIO_CONTINUOUS),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(8),\n    ZERO_PATTERN(&offset0xD5),\n    PATTERN_LENGTH(1),\n    NORMAL_BITRATE(64000),\n    MIME_TYPE(\"PCMA\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_pcm8000 = {\n    TYPE(PAYLOAD_AUDIO_CONTINUOUS),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(16),\n    ZERO_PATTERN(offset0),\n    PATTERN_LENGTH(1),\n    NORMAL_BITRATE(128000),\n    MIME_TYPE(\"PCM\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_l16_mono = {\n    TYPE(PAYLOAD_AUDIO_CONTINUOUS),\n    CLOCK_RATE(44100),\n    BITS_PER_SAMPLE(16),\n    ZERO_PATTERN(offset0),\n    PATTERN_LENGTH(2),\n    NORMAL_BITRATE(705600), /* (44100 x 16bits per frame x 1 channel) */\n    MIME_TYPE(\"L16\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_l16_stereo = {\n    TYPE(PAYLOAD_AUDIO_CONTINUOUS),\n    CLOCK_RATE(44100),\n    BITS_PER_SAMPLE(32), /* 16bits x 2 channels */\n    ZERO_PATTERN(offset0),\n    PATTERN_LENGTH(4),\n    NORMAL_BITRATE(1411200), /* (44100 x 16bits per frame x 2 channels) */\n    MIME_TYPE(\"L16\"),\n    CHANNELS(2),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_lpc1016 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(2400),\n    MIME_TYPE(\"1016\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_gsm = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(13500),\n    MIME_TYPE(\"GSM\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_lpc = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(5600), /* 20ms / 14 octets per frame */\n    MIME_TYPE(\"LPC\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_g7231 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(6300),\n    MIME_TYPE(\"G723\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_cn = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(8000),\n    MIME_TYPE(\"CN\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_g729 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(8000),\n    MIME_TYPE(\"G729\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_g7221 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(16000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(24000),\n    MIME_TYPE(\"G7221\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_g726_40 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(40000),\n    MIME_TYPE(\"G726-40\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_g726_32 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(32000),\n    MIME_TYPE(\"G726-32\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_g726_24 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(24000),\n    MIME_TYPE(\"G726-24\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_g726_16 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(16000),\n    MIME_TYPE(\"G726-16\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_aal2_g726_40 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(40000),\n    MIME_TYPE(\"AAL2-G726-40\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_aal2_g726_32 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(32000),\n    MIME_TYPE(\"AAL2-G726-32\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_aal2_g726_24 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(24000),\n    MIME_TYPE(\"AAL2-G726-24\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_aal2_g726_16 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(16000),\n    MIME_TYPE(\"AAL2-G726-16\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_mpv = {\n    TYPE(PAYLOAD_VIDEO), CLOCK_RATE(90000), BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),  PATTERN_LENGTH(0), NORMAL_BITRATE(256000),\n    MIME_TYPE(\"MPV\"),    CHANNELS(0),       RECV_FMTP(NULL),\n    SEND_FMTP(NULL),     NO_AVPF,           FLAGS(0),\n};\n\nPayloadType payload_type_h261 = {\n    TYPE(PAYLOAD_VIDEO), CLOCK_RATE(90000), BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),  PATTERN_LENGTH(0), NORMAL_BITRATE(0),\n    MIME_TYPE(\"H261\"),   CHANNELS(0),       RECV_FMTP(NULL),\n    SEND_FMTP(NULL),     NO_AVPF,           FLAGS(0),\n};\n\nPayloadType payload_type_h263 = {\n    TYPE(PAYLOAD_VIDEO), CLOCK_RATE(90000), BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),  PATTERN_LENGTH(0), NORMAL_BITRATE(256000),\n    MIME_TYPE(\"H263\"),   CHANNELS(0),       RECV_FMTP(NULL),\n    SEND_FMTP(NULL),     NO_AVPF,           FLAGS(0),\n};\n\nPayloadType payload_type_truespeech = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(8536),\n    MIME_TYPE(\"TSP0\"),\n    CHANNELS(0),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nRtpProfile av_profile;\n#ifdef __cplusplus\n}\n#endif\n\nvoid av_profile_init(RtpProfile *profile) {\n\trtp_profile_clear_all(profile);\n\tprofile->name = \"AV profile\";\n\n\trtp_profile_set_payload(profile, 0, &payload_type_pcmu8000);\n\trtp_profile_set_payload(profile, 1, &payload_type_lpc1016);\n\trtp_profile_set_payload(profile, 3, &payload_type_gsm);\n\trtp_profile_set_payload(profile, 7, &payload_type_lpc);\n\trtp_profile_set_payload(profile, 4, &payload_type_g7231);\n\trtp_profile_set_payload(profile, 8, &payload_type_pcma8000);\n\trtp_profile_set_payload(profile, 9, &payload_type_g722);\n\trtp_profile_set_payload(profile, 10, &payload_type_l16_stereo);\n\trtp_profile_set_payload(profile, 11, &payload_type_l16_mono);\n\trtp_profile_set_payload(profile, 13, &payload_type_cn);\n\trtp_profile_set_payload(profile, 18, &payload_type_g729);\n\trtp_profile_set_payload(profile, 31, &payload_type_h261);\n\trtp_profile_set_payload(profile, 32, &payload_type_mpv);\n\trtp_profile_set_payload(profile, 34, &payload_type_h263);\n}\n\n/* these are extra payload types that can be used dynamically */\nPayloadType payload_type_lpc1015 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(2400),\n    MIME_TYPE(\"1015\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_speex_nb = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(8000), /*not true: 8000 is the minimum*/\n    MIME_TYPE(\"speex\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_bv16 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(16000), /* 5ms / 80 bits per frame */\n    MIME_TYPE(\"BV16\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_speex_wb = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(16000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(28000),\n    MIME_TYPE(\"speex\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_speex_uwb = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(32000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(28000),\n    MIME_TYPE(\"speex\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_ilbc = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(13300), /* the minimum, with 30ms frames */\n    MIME_TYPE(\"iLBC\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_amr = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(12200),\n    MIME_TYPE(\"AMR\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_amrwb = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(16000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(23850),\n    MIME_TYPE(\"AMR-WB\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_gsm_efr = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED), CLOCK_RATE(8000),     BITS_PER_SAMPLE(0), ZERO_PATTERN(NULL), PATTERN_LENGTH(0),\n    NORMAL_BITRATE(12200),          MIME_TYPE(\"GSM-EFR\"), CHANNELS(1),\n};\n\nPayloadType payload_type_mp4v = {\n    TYPE(PAYLOAD_VIDEO),\n    CLOCK_RATE(90000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(0),\n    MIME_TYPE(\"MP4V-ES\"),\n    CHANNELS(0),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    AVPF(PAYLOAD_TYPE_AVPF_FIR | PAYLOAD_TYPE_AVPF_PLI, RTCP_DEFAULT_REPORT_INTERVAL),\n    FLAGS(0),\n};\n\nPayloadType payload_type_evrc0 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(0),\n    MIME_TYPE(\"EVRC0\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_evrcb0 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(0),\n    MIME_TYPE(\"EVRCB0\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_h263_1998 = {\n    TYPE(PAYLOAD_VIDEO),    CLOCK_RATE(90000), BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),     PATTERN_LENGTH(0), NORMAL_BITRATE(256000),\n    MIME_TYPE(\"H263-1998\"), CHANNELS(0),       RECV_FMTP(NULL),\n    SEND_FMTP(NULL),        NO_AVPF,           FLAGS(0),\n};\n\nPayloadType payload_type_h263_2000 = {\n    TYPE(PAYLOAD_VIDEO),    CLOCK_RATE(90000), BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),     PATTERN_LENGTH(0), NORMAL_BITRATE(0),\n    MIME_TYPE(\"H263-2000\"), CHANNELS(0),       RECV_FMTP(NULL),\n    SEND_FMTP(NULL),        NO_AVPF,           FLAGS(0),\n};\n\nPayloadType payload_type_theora = {\n    TYPE(PAYLOAD_VIDEO), CLOCK_RATE(90000), BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),  PATTERN_LENGTH(0), NORMAL_BITRATE(256000),\n    MIME_TYPE(\"theora\"), CHANNELS(0),       RECV_FMTP(NULL),\n    SEND_FMTP(NULL),     NO_AVPF,           FLAGS(0),\n};\n\nPayloadType payload_type_h264 = {\n    TYPE(PAYLOAD_VIDEO),\n    CLOCK_RATE(90000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(256000),\n    MIME_TYPE(\"H264\"),\n    CHANNELS(0),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    AVPF(PAYLOAD_TYPE_AVPF_FIR | PAYLOAD_TYPE_AVPF_PLI, RTCP_DEFAULT_REPORT_INTERVAL),\n    FLAGS(PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED),\n};\n\nPayloadType payload_type_h265 = {\n    TYPE(PAYLOAD_VIDEO),\n    CLOCK_RATE(90000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(256000),\n    MIME_TYPE(\"H265\"),\n    CHANNELS(0),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    AVPF(PAYLOAD_TYPE_AVPF_FIR | PAYLOAD_TYPE_AVPF_PLI, RTCP_DEFAULT_REPORT_INTERVAL),\n    FLAGS(PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED),\n};\n\nPayloadType payload_type_x_snow = {\n    TYPE(PAYLOAD_VIDEO), CLOCK_RATE(90000), BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),  PATTERN_LENGTH(0), NORMAL_BITRATE(256000),\n    MIME_TYPE(\"x-snow\"), CHANNELS(0),       RECV_FMTP(NULL),\n    SEND_FMTP(NULL),     NO_AVPF,           FLAGS(0),\n};\n\nPayloadType payload_type_jpeg = {\n    TYPE(PAYLOAD_VIDEO), CLOCK_RATE(90000), BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),  PATTERN_LENGTH(0), NORMAL_BITRATE(256000),\n    MIME_TYPE(\"JPEG\"),   CHANNELS(0),       RECV_FMTP(NULL),\n    SEND_FMTP(NULL),     NO_AVPF,           FLAGS(0),\n};\n\nPayloadType payload_type_vp8 = {\n    TYPE(PAYLOAD_VIDEO),\n    CLOCK_RATE(90000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(256000),\n    MIME_TYPE(\"VP8\"),\n    CHANNELS(0),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    AVPF(PAYLOAD_TYPE_AVPF_FIR | PAYLOAD_TYPE_AVPF_PLI | PAYLOAD_TYPE_AVPF_SLI | PAYLOAD_TYPE_AVPF_RPSI,\n         RTCP_DEFAULT_REPORT_INTERVAL),\n    FLAGS(PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED),\n};\n\nPayloadType payload_type_av1 = {\n    TYPE(PAYLOAD_VIDEO),\n    CLOCK_RATE(90000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(256000),\n    MIME_TYPE(\"AV1\"),\n    CHANNELS(0),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    AVPF(PAYLOAD_TYPE_AVPF_FIR | PAYLOAD_TYPE_AVPF_PLI, RTCP_DEFAULT_REPORT_INTERVAL), // TODO: LRR\n    FLAGS(PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED),\n};\n\nPayloadType payload_type_t140 = {\n    TYPE(PAYLOAD_TEXT), CLOCK_RATE(1000), BITS_PER_SAMPLE(0), ZERO_PATTERN(NULL), PATTERN_LENGTH(0), NORMAL_BITRATE(0),\n    MIME_TYPE(\"t140\"),  CHANNELS(0),      RECV_FMTP(NULL),    SEND_FMTP(NULL),    NO_AVPF,           FLAGS(0),\n};\n\nPayloadType payload_type_t140_red = {\n    TYPE(PAYLOAD_TEXT), CLOCK_RATE(1000), BITS_PER_SAMPLE(0), ZERO_PATTERN(NULL), PATTERN_LENGTH(0), NORMAL_BITRATE(0),\n    MIME_TYPE(\"red\"),   CHANNELS(0),      RECV_FMTP(NULL),    SEND_FMTP(NULL),    NO_AVPF,           FLAGS(0),\n};\n\nPayloadType payload_type_x_udpftp = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(1000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(0),\n    MIME_TYPE(\"x-udpftp\"),\n    CHANNELS(0),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_g722 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(64000),\n    MIME_TYPE(\"G722\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_silk_nb = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(13000),\n    MIME_TYPE(\"SILK\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_silk_mb = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(12000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(15000),\n    MIME_TYPE(\"SILK\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_silk_wb = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(16000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(20000),\n    MIME_TYPE(\"SILK\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_silk_swb = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(24000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(30000),\n    MIME_TYPE(\"SILK\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_aaceld_16k = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(16000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(24000),\n    MIME_TYPE(\"mpeg4-generic\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_aaceld_22k = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(22050),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(32000),\n    MIME_TYPE(\"mpeg4-generic\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_aaceld_32k = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(32000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(48000),\n    MIME_TYPE(\"mpeg4-generic\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_aaceld_44k = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(44100),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(64000),\n    MIME_TYPE(\"mpeg4-generic\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_aaceld_48k = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(48000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(64000),\n    MIME_TYPE(\"mpeg4-generic\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_opus = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(48000), /*mandatory according to RFC*/\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(20000),\n    MIME_TYPE(\"opus\"),\n    CHANNELS(2), /*mandatory according to RFC*/\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_isac = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(16000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(32000),\n    MIME_TYPE(\"iSAC\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(PAYLOAD_TYPE_IS_VBR),\n};\n\nPayloadType payload_type_codec2 = {\n    TYPE(PAYLOAD_AUDIO_PACKETIZED),\n    CLOCK_RATE(8000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(3200),\n    MIME_TYPE(\"CODEC2\"),\n    CHANNELS(1),\n    RECV_FMTP(NULL),\n    SEND_FMTP(NULL),\n    NO_AVPF,\n    FLAGS(0),\n};\n\nPayloadType payload_type_flexfec = {\n    TYPE(PAYLOAD_VIDEO),\n    CLOCK_RATE(90000),\n    BITS_PER_SAMPLE(0),\n    ZERO_PATTERN(NULL),\n    PATTERN_LENGTH(0),\n    NORMAL_BITRATE(3200),\n    MIME_TYPE(\"flexfec\"),\n    CHANNELS(0),\n    RECV_FMTP(\"repair-window=200000\"),\n    SEND_FMTP(\"repair-window=200000\"),\n    AVPF(PAYLOAD_TYPE_AVPF_FIR | PAYLOAD_TYPE_AVPF_PLI | PAYLOAD_TYPE_AVPF_SLI | PAYLOAD_TYPE_AVPF_RPSI,\n         RTCP_DEFAULT_REPORT_INTERVAL),\n    FLAGS(PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED)};\n"
  },
  {
    "path": "src/bandwidth-measurer.cc",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"ortp/utils.h\"\n\nnamespace ortp {\n\nclass BandwidthMeasurerBase {\npublic:\n\tvirtual ~BandwidthMeasurerBase() = default;\n\tvirtual void addBytes(size_t bytes, const struct timeval &tv) = 0;\n\tvirtual float computeBandwidth() = 0;\n};\n\n/**\n * Measure bandwidth over a considered period of time windowMs. The measure is updated continuously (sliding average).\n * This measurer sums bytes count into slots, where slots represents small time division of the considered\n * average interval.\n * This slots are used in a circular manner thanks to modulo operation.\n * Compared to an alternative solution where pairs of < byte counts, time > are stacked into a container\n * and then used to compute the average bandwidth, the choice of fixed divsion of time (slots) has the strong advantage\n * of not requiring any reallocation of memory during the processing.\n * The use of modulo avoids shifting arrays permanently.\n * Each addBytes() has almost constant-time complexity, and each computeBandwidth() call is O(n) where n is the number\n * of slots.\n */\ntemplate <int windowsMs, int numSlots>\nclass BandwidthMeasurer : public BandwidthMeasurerBase {\npublic:\n\tstatic_assert(windowsMs % numSlots == 0);\n\tstatic constexpr size_t slotMs = windowsMs / numSlots;\n\tstatic constexpr float timeNormalization = 1000.0f / (float)(windowsMs - slotMs);\n\tvoid addBytes(size_t bytes, const struct timeval &tv) override {\n\t\tsize_t absoluteIndex = moveToTimeval(tv);\n\t\tunsigned int relativeIndex = (unsigned int)(absoluteIndex % numSlots);\n\t\tmSlots[relativeIndex] += (int)bytes;\n\t}\n\tfloat computeBandwidth() override {\n\t\tsize_t ret = 0;\n\t\tstruct timeval current;\n\t\tbctbx_gettimeofday(&current, NULL);\n\t\tmoveToTimeval(current);\n\t\tfor (int i = 0; i < numSlots; ++i) {\n\t\t\tret += mSlots[i];\n\t\t}\n\t\t// The current slot may not be filled yet: we then choose to exclude it\n\t\t// to avoid a permanent bias. timeNormalization takes into account its exclusion.\n\t\tret -= mSlots[mCurrentAbsoluteIndex % numSlots];\n\t\treturn ((float)ret) * 8.0f * timeNormalization;\n\t}\n\nprivate:\n\tsize_t moveToTimeval(const struct timeval &tv) {\n\t\tsize_t newIndex = timevalToAbsIndex(tv);\n\t\tif (newIndex - mCurrentAbsoluteIndex >= numSlots) {\n\t\t\tmemset(&mSlots, 0, sizeof(mSlots));\n\t\t} else {\n\t\t\tfor (size_t i = mCurrentAbsoluteIndex + 1; i <= newIndex; ++i) {\n\t\t\t\tmSlots[i % numSlots] = 0;\n\t\t\t}\n\t\t}\n\t\tmCurrentAbsoluteIndex = newIndex;\n\t\treturn newIndex;\n\t}\n\tsize_t timevalToAbsIndex(const struct timeval &tv) {\n\t\treturn (size_t)((tv.tv_sec * 1000 + tv.tv_usec / 1000) / (windowsMs / numSlots));\n\t}\n\n\tint mSlots[numSlots];\n\tsize_t mCurrentAbsoluteIndex = 0;\n};\n\n} // namespace ortp\n\ntypedef struct _OrtpBandwidthMeasurer OrtpBandwidthMeasurer;\n\nOrtpBandwidthMeasurer *ortp_bandwidth_measurer_long_term_new(void) {\n\treturn (OrtpBandwidthMeasurer *)static_cast<ortp::BandwidthMeasurerBase *>(new ortp::BandwidthMeasurer<3000, 50>());\n}\n\nOrtpBandwidthMeasurer *ortp_bandwidth_measurer_short_term_new(void) {\n\treturn (OrtpBandwidthMeasurer *)static_cast<ortp::BandwidthMeasurerBase *>(new ortp::BandwidthMeasurer<1000, 50>());\n}\n\nvoid ortp_bandwidth_measurer_add_bytes(OrtpBandwidthMeasurer *obj, size_t bytes, const struct timeval *t) {\n\t((ortp::BandwidthMeasurerBase *)obj)->addBytes(bytes, *t);\n}\n\nfloat ortp_bandwidth_measurer_get_bandwdith(OrtpBandwidthMeasurer *obj) {\n\treturn ((ortp::BandwidthMeasurerBase *)obj)->computeBandwidth();\n}\n\nvoid ortp_bandwidth_measurer_destroy(OrtpBandwidthMeasurer *obj) {\n\tdelete (ortp::BandwidthMeasurerBase *)obj;\n}\n"
  },
  {
    "path": "src/congestiondetector.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"congestiondetector.h\"\n\n#include <math.h>\n#include <ortp/logging.h>\n\n#include <ortp/rtpsession.h>\n\nstatic const unsigned int congestion_pending_duration_ms = 5000;\nstatic const float return_from_suspected_max_loss_rate = 5.0;\nstatic const float absolute_congested_clock_ratio = 0.93f;\nstatic const float relative_congested_clock_ratio = 0.96f;\nstatic const float rls_forgetting_factor = 0.97f;\n\nconst char *ortp_congestion_detector_state_to_string(OrtpCongestionState state) {\n\tswitch (state) {\n\t\tcase CongestionStateNormal:\n\t\t\treturn \"CongestionStateNormal\";\n\t\t\tbreak;\n\t\tcase CongestionStateSuspected:\n\t\t\treturn \"CongestionStatePending\";\n\t\t\tbreak;\n\t\tcase CongestionStateDetected:\n\t\t\treturn \"CongestionStateDetected\";\n\t\t\tbreak;\n\t\tcase CongestionStateResolving:\n\t\t\treturn \"CongestionStateResolving\";\n\t\t\tbreak;\n\t}\n\treturn \"invalid state\";\n}\n\nstatic bool_t ortp_congestion_detector_set_state(OrtpCongestionDetector *cd, OrtpCongestionState state) {\n\tbool_t binary_state_changed = FALSE;\n\tif (state == cd->state) return FALSE;\n\tortp_message(\"OrtpCongestionDetector: moving from state %s to state %s\",\n\t             ortp_congestion_detector_state_to_string(cd->state), ortp_congestion_detector_state_to_string(state));\n\tcd->state = state;\n\tcd->too_much_loss = FALSE;\n\tif (state == CongestionStateDetected) {\n\t\tif (!cd->is_in_congestion) {\n\t\t\tcd->is_in_congestion = TRUE;\n\t\t\tbinary_state_changed = TRUE;\n\t\t}\n\t} else if (state == CongestionStateNormal) {\n\t\tcd->start_ms = (uint64_t)-1;\n\t\tif (cd->is_in_congestion) {\n\t\t\tcd->is_in_congestion = FALSE;\n\t\t\tbinary_state_changed = TRUE;\n\t\t}\n\t}\n\treturn binary_state_changed;\n}\n\nvoid ortp_congestion_detector_reset(OrtpCongestionDetector *cd) {\n\tcd->initialized = FALSE;\n\tcd->skip = FALSE;\n\tif (ortp_congestion_detector_set_state(cd, CongestionStateNormal)) {\n\t\tOrtpEvent *ev = ortp_event_new(ORTP_EVENT_CONGESTION_STATE_CHANGED);\n\t\tOrtpEventData *ed = ortp_event_get_data(ev);\n\t\ted->info.congestion_detected = FALSE;\n\t\trtp_session_dispatch_event(cd->session, ev);\n\t}\n}\n\nOrtpCongestionDetector *ortp_congestion_detector_new(RtpSession *session) {\n\tOrtpCongestionDetector *cd = (OrtpCongestionDetector *)ortp_malloc0(sizeof(OrtpCongestionDetector));\n\tcd->session = session;\n\tortp_congestion_detector_reset(cd);\n\treturn cd;\n}\n\n/*\nstatic uint32_t local_ts_to_remote_ts_rls(double clock_ratio, double offset, uint32_t local_ts){\n    return (uint32_t)( (int64_t)(clock_ratio*(double)local_ts) + (int64_t)offset);\n}\n*/\n\nstatic float ortp_congestion_detector_get_loss_rate(OrtpCongestionDetector *cd) {\n\tuint32_t cur_loss = (uint32_t)cd->session->stats.cum_packet_loss;\n\tuint32_t cur_seq = rtp_session_get_rcv_ext_seq_number(cd->session);\n\tuint32_t expected = cur_seq - cd->seq_begin;\n\n\tif (expected == 0) return 0;\n\treturn 100.0f * (float)(cur_loss - cd->loss_begin) / (float)expected;\n}\n\nbool_t ortp_congestion_detector_record(OrtpCongestionDetector *cd, uint32_t packet_ts, uint32_t cur_str_ts) {\n\tbool_t binary_state_changed = FALSE;\n\tbool_t clock_drift;\n\tJitterControl *jitterctl = &cd->session->rtp.jittctl;\n\t// float deviation;\n\n\tif (cd->skip) return FALSE;\n\n\tpacket_ts -= jitterctl->remote_ts_start;\n\tcur_str_ts -= jitterctl->local_ts_start;\n\n\tif (!cd->initialized) {\n\t\tcd->initialized = TRUE;\n\t\tortp_kalman_rls_init(&cd->rls, 1, packet_ts - cur_str_ts);\n\t\tcd->rls.lambda = rls_forgetting_factor;\n\t\tif (jitterctl->params.buffer_algorithm != OrtpJitterBufferRecursiveLeastSquare) {\n\t\t\tortp_error(\"ortp congestion detection requires RLS jitter buffer algorithm.\");\n\t\t\tcd->skip = TRUE;\n\t\t}\n\t}\n\n\tortp_kalman_rls_record(&cd->rls, cur_str_ts, packet_ts);\n\n\tif (cd->rls.m < 0) {\n\t\t/*\n\t\t * This can arrive when packets arrive in a very chaotic way during the first seconds of a call.\n\t\t * There is no usable information as long as the rls hasn't yet converged.\n\t\t */\n\t\treturn binary_state_changed;\n\t}\n\n\tclock_drift = cd->rls.m < absolute_congested_clock_ratio ||\n\t              jitterctl->capped_clock_ratio < absolute_congested_clock_ratio ||\n\t              cd->rls.m < relative_congested_clock_ratio * jitterctl->capped_clock_ratio;\n\t// deviation = ((int32_t)(packet_ts - local_ts_to_remote_ts_rls(cd->rls.m, cd->rls.b, cur_str_ts))) /\n\t// (float)jitterctl->clock_rate; deviation =\n\t// ortp_extremum_get_current(&jitterctl->max_ts_deviation)/(float)jitterctl->clock_rate; has_jitter = deviation >\n\t// acceptable_deviation;\n\n\t/*\n\tif (jitterctl->clock_rate == 90000){\n\t    ortp_message(\n\t        \"OrtpCongestionDetector state=%s clock=%f\"\n\t        \", jb->capped_clock_ratio=%f\"\n\t        \", down_bw=%0.f, up_bw=%0.f kbits/s\"\n\t        , ortp_congestion_detector_state_to_string(cd->state)\n\t        , cd->rls.m\n\t        , jitterctl->capped_clock_ratio\n\t        , rtp_session_get_recv_bandwidth_smooth(cd->session)*1e-3,\n\trtp_session_get_send_bandwidth_smooth(cd->session)*1e-3\n\t    );\n\t}\n\t*/\n\n\tswitch (cd->state) {\n\t\tcase CongestionStateNormal:\n\t\t\tif (clock_drift) {\n\t\t\t\tcd->start_ms = bctbx_get_cur_time_ms();\n\t\t\t\tcd->loss_begin = (uint32_t)cd->session->stats.cum_packet_loss;\n\t\t\t\tcd->seq_begin = rtp_session_get_rcv_ext_seq_number(cd->session);\n\t\t\t\tcd->last_packet_recv = cd->start_ms;\n\t\t\t\tbinary_state_changed = ortp_congestion_detector_set_state(cd, CongestionStateSuspected);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase CongestionStateSuspected: {\n\t\t\tuint64_t curtime = bctbx_get_cur_time_ms();\n\t\t\tif (!clock_drift) {\n\t\t\t\tfloat loss_rate = ortp_congestion_detector_get_loss_rate(cd);\n\t\t\t\tif (loss_rate >= return_from_suspected_max_loss_rate) {\n\t\t\t\t\tif (!cd->too_much_loss) {\n\t\t\t\t\t\tortp_message(\"OrtpCongestionDetector: loss rate is [%f], too much for returning to \"\n\t\t\t\t\t\t             \"CongestionStateNormal state.\",\n\t\t\t\t\t\t             loss_rate);\n\t\t\t\t\t\tcd->too_much_loss = TRUE;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// congestion has maybe stopped\n\t\t\t\t\tbinary_state_changed = ortp_congestion_detector_set_state(cd, CongestionStateNormal);\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\tif (curtime - cd->last_packet_recv >= 1000) {\n\t\t\t\t\t/*no packet received during last second !\n\t\t\t\t\t It means that the drift measure is not very significant, and futhermore the banwdith computation\n\t\t\t\t\t will be near to zero. It makes no sense to trigger a congestion detection in this case; the network\n\t\t\t\t\t is simply not working.\n\t\t\t\t\t */\n\t\t\t\t\tbinary_state_changed = ortp_congestion_detector_set_state(cd, CongestionStateNormal);\n\t\t\t\t} else {\n\t\t\t\t\t// congestion continues - if it has been for longer enough, trigger congestion flag\n\t\t\t\t\tif (curtime - cd->start_ms > congestion_pending_duration_ms) {\n\t\t\t\t\t\tbinary_state_changed = ortp_congestion_detector_set_state(cd, CongestionStateDetected);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcd->last_packet_recv = curtime;\n\t\t} break;\n\t\tcase CongestionStateDetected:\n\t\t\tif (!clock_drift) {\n\t\t\t\t// congestion is maybe terminated, go resolving state\n\t\t\t\tbinary_state_changed = ortp_congestion_detector_set_state(cd, CongestionStateResolving);\n\t\t\t\tcd->start_ms = bctbx_get_cur_time_ms();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase CongestionStateResolving:\n\t\t\tif (clock_drift) {\n\t\t\t\tbinary_state_changed = ortp_congestion_detector_set_state(cd, CongestionStateDetected);\n\t\t\t} else {\n\t\t\t\tif (bctbx_get_cur_time_ms() - cd->start_ms > congestion_pending_duration_ms) {\n\t\t\t\t\tbinary_state_changed = ortp_congestion_detector_set_state(cd, CongestionStateNormal);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t}\n\treturn binary_state_changed;\n}\n\nvoid ortp_congestion_detector_destroy(OrtpCongestionDetector *obj) {\n\tortp_free(obj);\n}"
  },
  {
    "path": "src/congestiondetector.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef CONGESTIONDETECTOR_H\n#define CONGESTIONDETECTOR_H\n\n#include <bctoolbox/list.h>\n#include <ortp/port.h>\n#include <ortp/utils.h>\nstruct _JitterControl;\n\ntypedef enum _OrtpCongestionState {\n\tCongestionStateNormal,\n\tCongestionStateSuspected,\n\tCongestionStateDetected,\n\tCongestionStateResolving\n} OrtpCongestionState;\n\ntypedef struct _OrtpCongestionDetector {\n\tOrtpKalmanRLS rls;\n\tuint64_t start_ms;\n\tuint64_t last_packet_recv;\n\tuint32_t loss_begin, seq_begin;\n\tbool_t initialized;\n\tbool_t is_in_congestion;\n\tbool_t skip;\n\tbool_t too_much_loss;\n\tOrtpCongestionState state;\n\tstruct _RtpSession *session;\n} OrtpCongestionDetector;\n\nOrtpCongestionDetector *ortp_congestion_detector_new(struct _RtpSession *session);\n\n/*\n * Returns TRUE if the congestion state is changed.\n **/\nbool_t ortp_congestion_detector_record(OrtpCongestionDetector *obj, uint32_t packet_ts, uint32_t cur_str_ts);\n\nvoid ortp_congestion_detector_destroy(OrtpCongestionDetector *obj);\n\nvoid ortp_congestion_detector_reset(OrtpCongestionDetector *cd);\n\n#endif\n"
  },
  {
    "path": "src/dblk.cc",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include <atomic>\n#include <ortp/port.h>\n#include <ortp/str_utils.h>\n\nusing namespace std;\n\nextern \"C\" {\ndblk_t *dblk_alloc(size_t size) {\n\tstruct datab *db;\n\tsize_t total_size = sizeof(struct datab) + size;\n\tdb = (struct datab *)ortp_malloc(total_size);\n\n\tdb->db_base = (uint8_t *)db + sizeof(struct datab);\n\tdb->db_lim = db->db_base + size;\n\tdb->db_ref = new atomic_int(1);\n\tdb->db_freefn = NULL; /* the buffer pointed by db_base must never be freed !*/\n\n\treturn db;\n}\nstruct datab *dblk_alloc2(uint8_t *buf, size_t size, void (*freefn)(void *)) {\n\tstruct datab *db;\n\tdb = (struct datab *)ortp_malloc(sizeof(struct datab));\n\n\tdb->db_base = buf;\n\tdb->db_lim = buf + size;\n\tdb->db_ref = new atomic_int(1);\n\tdb->db_freefn = freefn;\n\n\treturn db;\n}\n\nvoid dblk_ref(struct datab *data) {\n\tatomic_fetch_add_explicit(static_cast<atomic_int *>(data->db_ref), 1, memory_order_relaxed);\n}\n\nvoid dblk_unref(struct datab *data) {\n\tatomic_int previous_ref(\n\t    atomic_fetch_sub_explicit(static_cast<atomic_int *>(data->db_ref), 1, memory_order_release));\n\tif (previous_ref == 1) {\n\t\tif (data->db_freefn != NULL) data->db_freefn(data->db_base);\n\t\tdelete static_cast<atomic_int *>(data->db_ref);\n\t\tdata->db_ref = NULL;\n\t\tortp_free(data);\n\t}\n}\n\nint dblk_ref_value(dblk_t *db) {\n\treturn (int)static_cast<atomic_int *>(db->db_ref)->load();\n}\n\n} // extern \"C\""
  },
  {
    "path": "src/dll_entry.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include \"ortp/ortp.h\"\n\ntypedef struct __STRUCT_SHARED_DATA__ {\n\tDWORD m_nReference;\n#ifdef ORTP_WINDOWS_DESKTOP\n\tDWORD m_dwStartTime;\n#else\n\tULONGLONG m_ullStartTime;\n#endif\n\tBOOL m_bInitialize;\n\n} SHARED_DATA, *LPSHARED_DATA;\n\n#ifdef EXTERNAL_LOGGER\n#include \"logger.h\"\n#else\n#define RegisterLog(logVar, logString) ;\n#define UnregisterLog(logVar, logString) ;\n#endif\n\nextern DWORD dwoRTPLogLevel;\n\n#define SHMEMSIZE sizeof(SHARED_DATA)\n\n#ifndef ORTP_WINDOWS_DESKTOP\nstatic SHARED_DATA sharedData;\n#endif\nstatic LPSHARED_DATA lpSharedData;\nstatic HANDLE hMapObject = NULL; // handle to file mapping\n\nBOOL WINAPI DllMain(HINSTANCE hinstDLL, // handle to DLL module\n                    DWORD fdwReason,    // reason for calling function\n                    LPVOID lpReserved   // reserved\n) {\n\tBOOL fInit = FALSE;\n\tWORD wVersionRequested;\n\tWSADATA wsaData;\n\n\t// Perform actions based on the reason for calling.\n\tswitch (fdwReason) {\n\t\tcase DLL_PROCESS_ATTACH:\n\n#ifndef _UNICODE\n\t\t\tOutputDebugStringA(\"--> dll_entry.c - oRTP.dll - DLL_PROCESS_ATTACH()\\n\");\n#else\n\t\t\tOutputDebugStringW(L\"--> dll_entry.c - oRTP.dll - DLL_PROCESS_ATTACH()\\n\");\n#endif\n\n\t\t\twVersionRequested = MAKEWORD(1, 0);\n\n\t\t\tif (WSAStartup(wVersionRequested, &wsaData) != 0) {\n\t\t\t\treturn FALSE;\n\t\t\t}\n\n#ifdef ORTP_WINDOWS_DESKTOP\n\t\t\t// Create a named file mapping object.\n\t\t\thMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, // use paging file\n\t\t\t                               NULL,                 // default security attributes\n\t\t\t                               PAGE_READWRITE,       // read/write access\n\t\t\t                               0,                    // size: high 32-bits\n\t\t\t                               SHMEMSIZE,            // size: low 32-bits\n#ifdef ORTP_WINDOWS_UWP\n\t\t\t                               L\"oRTPSharedMemory\"); // name of map object\n#else\n\t\t\t                               \"oRTPSharedMemory\"); // name of map object\n#endif\n\n\t\t\tif (hMapObject == NULL) return FALSE;\n\n\t\t\t// The first process to attach initializes memory.\n\t\t\tfInit = (GetLastError() != ERROR_ALREADY_EXISTS);\n\n\t\t\t// Get a pointer to the file-mapped shared memory.\n\n\t\t\tlpSharedData = (LPSHARED_DATA)MapViewOfFile(hMapObject,     // object to map view of\n\t\t\t                                            FILE_MAP_WRITE, // read/write access\n\t\t\t                                            0,              // high offset:  map from\n\t\t\t                                            0,              // low offset:   beginning\n\t\t\t                                            0);             // default: map entire file\n\t\t\tif (lpSharedData == NULL) return FALSE;\n#else\n\t\t\tfInit = TRUE;\n\t\t\tlpSharedData = &sharedData;\n#endif\n\n\t\t\t// Initialize memory if this is the first process.\n\n\t\t\tif (fInit) {\n#ifndef _UNICODE\n\t\t\t\tOutputDebugStringA(\"--> dll_entry.c - oRTP.dll - Initializing module\\n\");\n#else\n\t\t\t\tOutputDebugStringW(L\"--> dll_entry.c - oRTP.dll - Initializing module\\n\");\n#endif\n\n#ifdef ORTP_WINDOWS_DESKTOP\n\t\t\t\tlpSharedData->m_dwStartTime = GetTickCount();\n#else\n\t\t\t\tlpSharedData->m_ullStartTime = GetTickCount64();\n#endif\n\t\t\t\tlpSharedData->m_nReference = 1;\n\t\t\t\tlpSharedData->m_bInitialize = FALSE;\n\n\t\t\t\t// Register the log\n\t\t\t\tRegisterLog(&dwoRTPLogLevel, \"LOG_ORTP\");\n\t\t\t} else {\n#ifndef _UNICODE\n\t\t\t\tOutputDebugStringA(\"--> dll_entry.c - oRTP.dll - Binding\\n\");\n#else\n\t\t\t\tOutputDebugStringW(L\"--> dll_entry.c - oRTP.dll - Binding\\n\");\n#endif\n\t\t\t\tlpSharedData->m_nReference++;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase DLL_THREAD_ATTACH:\n\n\t\t\tif (lpSharedData != NULL) {\n\t\t\t\tif (lpSharedData->m_bInitialize == FALSE) {\n\t\t\t\t\t// Initialize oRTP\n\t\t\t\t\tortp_init();\n\n\t\t\t\t\t// Start the scheduler\n\t\t\t\t\t// ortp_scheduler_init();\n\n\t\t\t\t\tlpSharedData->m_bInitialize = TRUE;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase DLL_THREAD_DETACH:\n\t\t\tbreak;\n\n\t\tcase DLL_PROCESS_DETACH:\n\n\t\t\tif (lpSharedData != NULL) {\n#ifndef _UNICODE\n\t\t\t\tOutputDebugStringA(\"--> dll_entry.c - oRTP.dll - Binding\\n\");\n#else\n\t\t\t\tOutputDebugStringW(L\"--> dll_entry.c - oRTP.dll - Binding\\n\");\n#endif\n\t\t\t\tlpSharedData->m_nReference--;\n\n\t\t\t\tif (lpSharedData->m_nReference == 0) {\n#ifndef _UNICODE\n\t\t\t\t\tOutputDebugStringA(\"--> dll_entry.c - oRTP.dll - Detaching\\n\");\n#else\n\t\t\t\t\tOutputDebugStringW(L\"--> dll_entry.c - oRTP.dll - Detaching\\n\");\n#endif\n\n\t\t\t\t\tortp_exit();\n\t\t\t\t\tUnregisterLog(&dwoRTPLogLevel, \"LOG_ORTP\");\n\n#ifdef ORTP_WINDOWS_DESKTOP\n\t\t\t\t\t// Unmap shared memory from the process's address space.\n\t\t\t\t\tUnmapViewOfFile(lpSharedData);\n\t\t\t\t\tlpSharedData = NULL;\n\n\t\t\t\t\t// Close the process's handle to the file-mapping object.\n\t\t\t\t\tCloseHandle(hMapObject);\n\t\t\t\t\thMapObject = INVALID_HANDLE_VALUE;\n#endif\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t}\n\n\treturn TRUE; // Successful DLL_PROCESS_ATTACH.\n}\n"
  },
  {
    "path": "src/event.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include \"ortp/event.h\"\n#include \"ortp/ortp.h\"\n#include \"ortp/str_utils.h\"\n#include \"utils.h\"\n\nOrtpEvent *ortp_event_new(unsigned long type) {\n\tOrtpEventData *ed;\n\tconst int size = sizeof(OrtpEventType) + sizeof(OrtpEventData);\n\tmblk_t *m = allocb(size, 0);\n\tmemset(m->b_wptr, 0, size);\n\t*((OrtpEventType *)m->b_wptr) = type;\n\ted = ortp_event_get_data(m);\n\tortp_get_cur_time(&ed->ts);\n\treturn m;\n}\n\nOrtpEvent *ortp_event_dup(OrtpEvent *ev) {\n\tOrtpEvent *nev = ortp_event_new(ortp_event_get_type(ev));\n\tOrtpEventData *ed = ortp_event_get_data(ev);\n\tOrtpEventData *edv = ortp_event_get_data(nev);\n\tmemcpy(edv, ed, sizeof(OrtpEventData));\n\tif (ed->packet) edv->packet = copymsg(ed->packet);\n\treturn nev;\n}\n\nOrtpEventType ortp_event_get_type(const OrtpEvent *ev) {\n\treturn ((OrtpEventType *)ev->b_rptr)[0];\n}\n\nOrtpEventData *ortp_event_get_data(OrtpEvent *ev) {\n\treturn (OrtpEventData *)(ev->b_rptr + sizeof(OrtpEventType));\n}\n\nvoid ortp_event_destroy(OrtpEvent *ev) {\n\tOrtpEventData *d = ortp_event_get_data(ev);\n\tif (dblk_ref_value(ev->b_datap) == 1) {\n\t\tif (d->packet) freemsg(d->packet);\n\t}\n\tfreemsg(ev);\n}\n\nOrtpEvQueue *ortp_ev_queue_new(void) {\n\tOrtpEvQueue *q = ortp_new(OrtpEvQueue, 1);\n\tqinit(&q->q);\n\tortp_mutex_init(&q->mutex, NULL);\n\treturn q;\n}\n\nvoid ortp_ev_queue_flush(OrtpEvQueue *qp) {\n\tOrtpEvent *ev;\n\twhile ((ev = ortp_ev_queue_get(qp)) != NULL) {\n\t\tortp_event_destroy(ev);\n\t}\n}\n\nOrtpEvent *ortp_ev_queue_get(OrtpEvQueue *q) {\n\tOrtpEvent *ev;\n\tortp_mutex_lock(&q->mutex);\n\tev = getq(&q->q);\n\tortp_mutex_unlock(&q->mutex);\n\treturn ev;\n}\n\nvoid ortp_ev_queue_destroy(OrtpEvQueue *qp) {\n\tortp_ev_queue_flush(qp);\n\tortp_mutex_destroy(&qp->mutex);\n\tortp_free(qp);\n}\n\nvoid ortp_ev_queue_put(OrtpEvQueue *q, OrtpEvent *ev) {\n\tortp_mutex_lock(&q->mutex);\n\tputq(&q->q, ev);\n\tortp_mutex_unlock(&q->mutex);\n}\n\nstatic bool_t rtcp_is_type(const mblk_t *m, rtcp_type_t type) {\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\treturn (ch != NULL && rtcp_common_header_get_packet_type(ch) == type);\n}\n\nOrtpEvDispatcher *ortp_ev_dispatcher_new(RtpSession *session) {\n\tOrtpEvDispatcher *d = ortp_new(OrtpEvDispatcher, 1);\n\td->session = session;\n\td->q = ortp_ev_queue_new();\n\trtp_session_register_event_queue(session, d->q);\n\td->cbs = NULL;\n\n\treturn d;\n}\n\nvoid ortp_ev_dispatcher_destroy(OrtpEvDispatcher *d) {\n\tOList *it;\n\tfor (it = d->cbs; it != NULL; it = it->next) {\n\t\tortp_free(it->data);\n\t}\n\to_list_free(d->cbs);\n\trtp_session_unregister_event_queue(d->session, d->q);\n\tortp_ev_queue_destroy(d->q);\n\tortp_free(d);\n}\n\nstatic bool_t is_rtcp_event(OrtpEventType type) {\n\treturn (type == ORTP_EVENT_RTCP_PACKET_RECEIVED || type == ORTP_EVENT_RTCP_PACKET_EMITTED);\n}\n\nstatic void analyse_event_and_invoke(OrtpEvDispatcher *disp, OrtpEvent *ev) {\n\tOrtpEventData *d = ortp_event_get_data(ev);\n\tOrtpEventType evt = ortp_event_get_type(ev);\n\tbool_t is_rtcp =\n\t    (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED || evt == ORTP_EVENT_RTCP_PACKET_EMITTED) && d->packet != NULL;\n\tdo {\n\t\t/*for each packet part, if ANY iterate through the whole callback list to see if\n\t\tanyone is interested in it*/\n\n\t\tOList *it;\n\t\tfor (it = disp->cbs; it != NULL; it = it->next) {\n\t\t\tOrtpEvDispatcherData *data = (OrtpEvDispatcherData *)it->data;\n\t\t\tif (evt == data->type) {\n\t\t\t\tif (!is_rtcp_event(data->type) || (is_rtcp && rtcp_is_type(d->packet, data->subtype))) {\n\t\t\t\t\tdata->on_found(d, data->user_data);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} while (is_rtcp && _rtcp_next_packet(d->packet));\n}\n\nvoid ortp_ev_dispatcher_iterate(OrtpEvDispatcher *d) {\n\tOrtpEvent *ev = NULL;\n\twhile ((ev = ortp_ev_queue_get(d->q)) != NULL) {\n\t\tanalyse_event_and_invoke(d, ev);\n\t\tortp_event_destroy(ev);\n\t}\n}\n\nvoid ortp_ev_dispatcher_connect(\n    OrtpEvDispatcher *d, OrtpEventType type, rtcp_type_t subtype, OrtpEvDispatcherCb cb, void *user_data) {\n\tOrtpEvDispatcherData *data = ortp_new(OrtpEvDispatcherData, 1);\n\tdata->type = type;\n\tdata->subtype = subtype;\n\tdata->on_found = cb;\n\tdata->user_data = user_data;\n\td->cbs = o_list_append(d->cbs, data);\n}\n\nvoid ortp_ev_dispatcher_disconnect(OrtpEvDispatcher *d,\n                                   OrtpEventType type,\n                                   rtcp_type_t subtype,\n                                   OrtpEvDispatcherCb cb) {\n\tOList *it = NULL;\n\tif (!d) {\n\t\treturn;\n\t}\n\tit = d->cbs;\n\twhile (it) {\n\t\tOrtpEvDispatcherData *data = (OrtpEvDispatcherData *)it->data;\n\t\tif (data && data->type == type && data->subtype == subtype && data->on_found == cb) {\n\t\t\tOList *tofree = it;\n\t\t\tit = it->next;\n\t\t\tortp_free(data);\n\t\t\td->cbs = o_list_remove_link(d->cbs, tofree);\n\t\t} else {\n\t\t\tit = it->next;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/extremum.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <bctoolbox/defs.h>\n\n#include \"ortp/logging.h\"\n#include \"ortp/utils.h\"\n\nvoid ortp_extremum_reset(OrtpExtremum *obj) {\n\tobj->current_extremum = 0;\n\tobj->extremum_time = (uint64_t)-1;\n\tobj->last_stable = 0;\n}\n\nvoid ortp_extremum_init(OrtpExtremum *obj, int period) {\n\tortp_extremum_reset(obj);\n\tobj->period = period;\n}\n\nstatic bool_t extremum_check_init(OrtpExtremum *obj, uint64_t curtime, float value, BCTBX_UNUSED(const char *kind)) {\n\tif (obj->extremum_time != (uint64_t)-1) {\n\t\tif (((int)(curtime - obj->extremum_time)) > obj->period) {\n\t\t\tobj->last_stable = obj->current_extremum;\n\t\t\t/*last extremum is too old, drop it and replace it with current value*/\n\t\t\tobj->current_extremum = value;\n\t\t\tobj->extremum_time = curtime;\n\t\t\treturn TRUE;\n\t\t}\n\t} else {\n\t\tobj->last_stable = value;\n\t\tobj->current_extremum = value;\n\t\tobj->extremum_time = curtime;\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n}\n\nbool_t ortp_extremum_record_min(OrtpExtremum *obj, uint64_t curtime, float value) {\n\tbool_t ret = extremum_check_init(obj, curtime, value, \"min\");\n\tif (value < obj->current_extremum) {\n\t\tobj->last_stable = obj->current_extremum;\n\t\tobj->current_extremum = value;\n\t\tobj->extremum_time = curtime;\n\t\treturn TRUE;\n\t}\n\treturn ret;\n}\n\nbool_t ortp_extremum_record_max(OrtpExtremum *obj, uint64_t curtime, float value) {\n\tbool_t ret = extremum_check_init(obj, curtime, value, \"max\");\n\tif (value > obj->current_extremum) {\n\t\tobj->last_stable = obj->current_extremum;\n\t\tobj->current_extremum = value;\n\t\tobj->extremum_time = curtime;\n\t\tret = TRUE;\n\t}\n\treturn ret;\n}\n\nfloat ortp_extremum_get_current(OrtpExtremum *obj) {\n\treturn obj->current_extremum;\n}\n\nfloat ortp_extremum_get_previous(OrtpExtremum *obj) {\n\treturn obj->last_stable;\n}\n"
  },
  {
    "path": "src/fecstream/fec-encoder.cpp",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"fec-encoder.h\"\n\nusing namespace ortp;\n\nFecEncoder::FecEncoder(FecParamsController *params) {\n\tupdateProtectionParam(params->getL(), params->getD(), params->is2D());\n\tmLoading = 0;\n}\n\nvoid FecEncoder::updateProtectionParam(uint8_t L, uint8_t D, bool is2D) {\n\tif (L == 0) {\n\t\tmIs2D = false;\n\t\tmL = 0;\n\t\tmD = 0;\n\t\tmRows = 0;\n\t\tmColumns = 0;\n\t\tmRowRepairNb = 0;\n\t\tmColRepairNb = 0;\n\t\tortp_message(\"[flexfec] wrong parameters: L = 0, should be > 0. No repair packets sent.\");\n\t\treturn;\n\t}\n\tmIs2D = is2D;\n\tmL = L;\n\tmD = D;\n\tif (D == 0) {\n\t\tmIs2D = false;\n\t}\n\n\tif (mIs2D) {\n\t\tmRows = D;\n\t\tmColumns = L;\n\t\tmRowRepairNb = D;\n\t\tmColRepairNb = L;\n\t} else {\n\t\tif (D > 1) {\n\t\t\tmRows = D;\n\t\t\tmColumns = L;\n\t\t\tmRowRepairNb = 0;\n\t\t\tmColRepairNb = L;\n\t\t} else {\n\t\t\tmRows = 1;\n\t\t\tmColumns = L;\n\t\t\tmRowRepairNb = 1;\n\t\t\tmColRepairNb = 0;\n\t\t}\n\t}\n}\n\nvoid FecEncoder::init(struct _RtpSession *fecSession, struct _RtpSession *sourceSession) {\n\tthis->mFecSession = fecSession;\n\tthis->mSourceSession = sourceSession;\n\tinitRowRepairPackets();\n\tinitColRepairPackets();\n\tmLoading = 0;\n}\n\nvoid FecEncoder::update(uint8_t L, uint8_t D, bool is2D) {\n\tupdateProtectionParam(L, D, is2D);\n\tclear();\n\tinitRowRepairPackets();\n\tinitColRepairPackets();\n}\n\nvoid FecEncoder::clear() {\n\tmRowRepair.clear();\n\tmColRepair.clear();\n\tmLoading = 0;\n}\n\nvoid FecEncoder::initRowRepairPackets() {\n\tuint16_t seqnum = 0U;\n\tint D = (mIs2D) ? 1 : 0;\n\tfor (int i = 0; i < mRowRepairNb; i++) {\n\t\tauto repair = std::make_shared<FecRepairPacket>(mFecSession, mSourceSession, seqnum, mL, D);\n\t\tmRowRepair.emplace_back(repair);\n\t\tseqnum += mColumns;\n\t}\n}\n\nvoid FecEncoder::initColRepairPackets() {\n\tuint16_t seqnum = 0U;\n\tfor (int i = 0; i < mColRepairNb; i++) {\n\t\tauto repair = std::make_shared<FecRepairPacket>(mFecSession, mSourceSession, seqnum, mL, mD);\n\t\tmColRepair.emplace_back(repair);\n\t\tseqnum++;\n\t}\n}\n\nvoid FecEncoder::reset(uint16_t nextSequenceNumber) {\n\tmLoading = 0;\n\tif (mRowRepairNb > 0) {\n\t\tresetRowRepairPackets(nextSequenceNumber);\n\t}\n\tif (mColRepairNb > 0) {\n\t\tresetColRepairPackets(nextSequenceNumber);\n\t}\n}\n\nvoid FecEncoder::resetRowRepairPackets(uint16_t seqnumBase) {\n\tuint16_t seqnum = seqnumBase;\n\tfor (size_t i = 0; i < mRowRepair.size(); i++) {\n\t\tmRowRepair[i]->reset(seqnum);\n\t\tseqnum += mColumns;\n\t}\n}\n\nvoid FecEncoder::resetColRepairPackets(uint16_t seqnumBase) {\n\tuint16_t seqnum = seqnumBase;\n\tfor (size_t i = 0; i < mColRepair.size(); i++) {\n\t\tmColRepair[i]->reset(seqnum);\n\t\tseqnum++;\n\t}\n}\n\nvoid FecEncoder::add(FecSourcePacket const &packet) {\n\tmLoading++;\n\tint i = getCurrentRow();\n\tint j = getCurrentColumn();\n\tif (mRowRepairNb > 0) {\n\t\tmRowRepair[i]->add(packet);\n\t}\n\tif (mColRepairNb > 0) {\n\t\tmColRepair[j]->add(packet);\n\t}\n}\n\nbool FecEncoder::isFull() const {\n\treturn mLoading == mRows * mColumns && mLoading != 0;\n}\n\nbool FecEncoder::isEmpty() const {\n\treturn mLoading == 0;\n}\n\nint FecEncoder::getCurrentRow() const {\n\tif (mD > 1) {\n\t\treturn ((mLoading - 1) / mColumns);\n\t} else {\n\t\treturn 0;\n\t}\n}\n\nint FecEncoder::getCurrentColumn() const {\n\treturn ((mLoading - 1) % mColumns);\n}\n\nbool FecEncoder::isRowFull() const {\n\tif (mRowRepairNb == 0) return false;\n\telse return (getCurrentColumn() == (mColumns - 1));\n}\n\nbool FecEncoder::isColFull() const {\n\tif (mColRepairNb == 0) return false;\n\treturn (getCurrentRow() == (mRows - 1));\n}\n\nstd::shared_ptr<FecRepairPacket> FecEncoder::getRowRepair(int i) const {\n\tif (i < static_cast<int>(mRowRepair.size())) return mRowRepair[i];\n\telse return nullptr;\n}\n\nstd::shared_ptr<FecRepairPacket> FecEncoder::getColRepair(int i) const {\n\tif (i < static_cast<int>(mColRepair.size())) return mColRepair[i];\n\telse return nullptr;\n}\n\nmblk_t *FecEncoder::getRowRepairMblk(int i) const {\n\tif (i < static_cast<int>(mRowRepair.size())) return mRowRepair[i]->getCopy();\n\telse return nullptr;\n}\n\nmblk_t *FecEncoder::getColRepairMblk(int i) const {\n\tif (i < static_cast<int>(mColRepair.size())) return mColRepair[i]->getCopy();\n\telse return nullptr;\n}"
  },
  {
    "path": "src/fecstream/fec-encoder.h",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef FEC_ENCODER_H\n#define FEC_ENCODER_H\n\n#include <memory>\n#include <vector>\n\n#include \"fec-params.h\"\n#include \"packet-api.h\"\n\nnamespace ortp {\n\n#ifdef _WIN32\n// Disable C4251 triggered by need to export all stl template classes\n#pragma warning(disable : 4251)\n#endif // ifdef _WIN32\n\nclass FecParamsController;\n\n/** @class FecEncoder\n * @brief Class to apply parity codes on source packets to generate the flexible FEC repair packets.\n *\n *  This class combines the source packets that are sent to the encoder, following a given FEC protection configuration\n * represented as a FEC block with D rows and L colmuns, as described in RFC 8627. The combination can be:\n * - 1D non interleaved parity protection or row protection: L successive source packets are combined together on the\n * same row, and 1 row repair packet is generated.\n * - 1D interleaved parity protection, or column protection: D source packets are combined together every L packet, on\n * the same column, and 1 column repair packet is generated per column. It leads to L column repair packets for L\n * independent columns, for a set of L*D source packets, managed by the same FecEncoder.\n * - 2D parity protection : both row and column protection are applied, and D row repair packets and L column repair\n * packets are generated.\n * The source packets are objects of the FecSourcePacket class, and repair packets are objects of the FecRepairPacket\n * class. When all repair packets have been generated, the encoder is full and can be reset to receive the next set of\n * source packets. This set is identified by the sequence number of the first source packet. The encoder can be updated\n * to a new protection configuration, by changing the values of L and D and switching between 1D and 2D.\n */\nclass ORTP_PUBLIC FecEncoder {\n\npublic:\n\tFecEncoder(){};\n\tFecEncoder(FecParamsController *parameters);\n\n\t/**\n\t * @brief Initialize the FEC encoder.\n\t *\n\t * Set the source and FEC RTP sessions and create empty repair packets.\n\t *\n\t * @param fecSession RTP session to which the stream where the FEC repair packets are sent belongs.\n\t * @param sourceSession RTP session to which the stream where the source packets are sent belongs.\n\t */\n\tvoid init(struct _RtpSession *fecSession, struct _RtpSession *sourceSession);\n\n\t/**\n\t * @brief Update and initialize the encoder with given parameters.\n\t *\n\t * Update of the encoder to a new protection configuration. The repair packet lists are cleared and new empty repair\n\t * packets are generated following the new encoder protection.\n\t *\n\t * @param L uint8_t Number of columns in row protection, must be strictly positive.\n\t * @param D uint8_t Number of rows in columns protection.\n\t */\n\tvoid update(uint8_t L, uint8_t D, bool is2D);\n\n\t/**\n\t * Clear the vectors of repair packets and set the position of the last source packet to 0.\n\t *\n\t */\n\tvoid clear();\n\n\t/**\n\t * Reset the encoder: the repair packets are reset to initial values, with empty payload. The sequence\n\t * number base of the FEC block is set to nextSequenceNumber. The current value of mLoading is set to 0. The\n\t * protection parameters L and D and the FEC block dimension remain the same.\n\t *\n\t * @param nextSequenceNumber uint16_t Sequence number of the first source packet that will be protected by the\n\t * FEC block.\n\t */\n\tvoid reset(uint16_t nextSequenceNumber);\n\n\t/**\n\t * @brief Add a source packet to the FEC encoder.\n\t *\n\t * The add() method of the FecRepairPacket class is called to compute the payload and the bitstring. The repair\n\t * packets that protect this packet in the FEC block are updated:\n\t *  - the row repair packet of the current row in 1D non interleaved protection\n\t *  - the column repair packet of the current column in the 1D interleaved protection\n\t *  - both row and column repair packets in 2D protection.\n\t * The mLoading value is incremented by 1.\n\t *\n\t * @param packet source packet to add.\n\t */\n\tvoid add(FecSourcePacket const &packet);\n\n\t/**\n\t * Check if all source packets of the FEC block have been added.\n\t *\n\t * @return true if the encoder is full, false otherwise.\n\t */\n\tbool isFull() const;\n\n\t/**\n\t * @brief Determine if the encoder is empty.\n\t *\n\t * This function checks if no source packet has been added yet.\n\t *\n\t * @return true if the encoder is empty, false otherwise.\n\t */\n\tbool isEmpty() const;\n\n\t/**\n\t * Return the index of the row in the FEC block where the last source packet has been added.\n\t *\n\t * @return index of the last row filled.\n\t */\n\tint getCurrentRow() const;\n\n\t/**\n\t * Return the index of the column in the FEC block where the last source packet has been added.\n\t *\n\t * @return index of the last column filled.\n\t */\n\tint getCurrentColumn() const;\n\n\t/**\n\t * Check if L source packets have been added in the current row of the FEC block. In 1D interleaved parity\n\t * protection, return false.\n\t *\n\t * @return true if all source packets have been added in the current row, false otherwise.\n\t */\n\tbool isRowFull() const;\n\n\t/**\n\t * Check if D source packets have been added in the current column of the FEC block. In 1D non interleaved\n\t * case, return false.\n\t *\n\t * @return true if all source packets have been added in the current column, false otherwise.\n\t */\n\tbool isColFull() const;\n\n\t/**\n\t * Return a the row repair packet that protects the row i.\n\t *\n\t * @param i index of the row repair packet.\n\t * @return row repair packet or nullptr if the packet doesn't exist.\n\t */\n\tstd::shared_ptr<FecRepairPacket> getRowRepair(int i) const;\n\n\t/**\n\t * Return a the column repair packet that protects the column i.\n\t *\n\t * @param i index of the column repair packet.\n\t * @return column repair packet or nullptr if the packet doesn't exist.\n\t */\n\tstd::shared_ptr<FecRepairPacket> getColRepair(int i) const;\n\n\t/**\n\t * Return a copy of the mblkt of the repair packet that protects the row i.\n\t *\n\t * @param i index of the repair packet.\n\t * @return copy of the mblkt of the repair packet or nullptr if the packet doesn't exist.\n\t */\n\tmblk_t *getRowRepairMblk(int i) const;\n\n\t/**\n\t * Return a copy of the mblkt of the repair packet that protects the column i.\n\t *\n\t * @param i index of the repair packet.\n\t * @return copy of the mblkt of the repair packet or nullptr if the packet doesn't exist.\n\t */\n\tmblk_t *getColRepairMblk(int i) const;\n\nprivate:\n\t/**\n\t * @brief Update encoder protection configuration.\n\t *\n\t * Change the parameters mL, mD, mColumns, mRows, mRowRepairNb, mColRepairNb and mIs2D to set a new parity\n\t * protection configuration, following the values of L, D and is2D.\n\t *\n\t * @param L number of rows of the FEC block. L must be stricly positive. If D <= 1, or if D > 1 and is2D is true,\n\t * the row protection is applied and L row repair packets are generated.\n\t * @param D number of columns of the FEC block. If D = 0, the 1D row protection is always applied, no column\n\t * repair packet is generated.\n\t * @param is2D boolean, true for 2D parity protection, false otherwise.\n\t */\n\tvoid updateProtectionParam(uint8_t L, uint8_t D, bool is2D);\n\n\t/**\n\t * Initialize the vector of row repair packets: generate mRowRepairNb empty row repair packets, with the sequence\n\t * base numbers 0, ... i*L, ... (D-1)*L.\n\t */\n\tvoid initRowRepairPackets();\n\n\t/**\n\t * Initialize the vector of column repair packets: generate mColRepairNb empty column repair packets, with the\n\t * sequence base numbers 0, ... i, ... (L-1).\n\t */\n\tvoid initColRepairPackets();\n\n\t/**\n\t * Reset the row repair packets and their sequence base numbers of the row repair packets, to make the FEC block\n\t * starts at seqnumBase. Then the mRowRepairNb row repair packets protect the rows for source packets starting with\n\t * the sequence numbers seqnumBase, ... seqnumBase + i*L, ... seqnumBase + (D-1)*L.\n\t *\n\t * @param seqnumBase sequence number base: sequence number of the first source packet protected by a repair\n\t * packet.\n\t */\n\tvoid resetRowRepairPackets(uint16_t seqnumBase);\n\n\t/**\n\t * Reset the column repair packets and their the sequence base number, to make the FEC block starts at seqnumBase.\n\t * Then the mRowRepairNb column repair packets protect the columns for source packets starting with the sequence\n\t * numbers seqnumBase, ... seqnumBase + i, ... seqnumBase + L - 1.\n\t *\n\t * @param seqnumBase sequence number base: sequence number of the first source packet protected by a repair\n\t * packet.\n\t */\n\tvoid resetColRepairPackets(uint16_t seqnumBase);\n\n\tstd::vector<std::shared_ptr<FecRepairPacket>> mRowRepair; /**< The repair packets that protects the rows, in\n\t                                                         increasing order of base sequence number. */\n\tstd::vector<std::shared_ptr<FecRepairPacket>> mColRepair; /**< The repair packets that\n\t                   protects the columns, in increasing order of base sequence number. */\n\tRtpSession *mFecSession;    /**< RTP session to which the source and FEC streams belongs. */\n\tRtpSession *mSourceSession; /**< RTP source session. */\n\tint mLoading;               /**< Current position of the last source packet received in the FEC block. */\n\tuint8_t mL;                 /**< Number of columns of the FEC block (length of each row).*/\n\tuint8_t mD;   /**< Number of rows of the FEC block (depth of the FEC protection). For 1D non interleaved protection,\n\t                 mD = 0.*/\n\tint mColumns; /**< Number of columns of the FEC block.*/\n\tint mRows;    /**< Number of rows of the FEC block. */\n\tint mRowRepairNb; /**< Number of repair packets that protects rows. */\n\tint mColRepairNb; /**< Number of repair packets that protects columns. */\n\tbool mIs2D;       /**< True for a 2D parity protection, false otherwise. */\n};\n} // namespace ortp\n#endif // FEC_ENCODER_H"
  },
  {
    "path": "src/fecstream/fec-packets-connection.cpp",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"fec-packets-connection.h\"\n\nusing namespace ortp;\n\nFecSourceNode::FecSourceNode(const uint16_t seqNum) : mSeqNum(seqNum) {\n}\n\nvoid FecSourceNode::addRowRepair(const uint16_t seqNum) {\n\tmRowRepairSeqNum.insert(seqNum);\n}\n\nvoid FecSourceNode::addColRepair(const uint16_t seqNum) {\n\tmColRepairSeqNum.insert(seqNum);\n}\n\nbool FecSourceNode::removeRowRepair(const uint16_t repairSeqNum) {\n\tmRowRepairSeqNum.erase(repairSeqNum);\n\treturn mRowRepairSeqNum.size() == 0 && mColRepairSeqNum.size() == 0;\n}\n\nbool FecSourceNode::removeColRepair(const uint16_t repairSeqNum) {\n\tmColRepairSeqNum.erase(repairSeqNum);\n\treturn mRowRepairSeqNum.size() == 0 && mColRepairSeqNum.size() == 0;\n}\n\nFecRepairNode::FecRepairNode(const std::vector<uint16_t> sequenceNumberProtected)\n    : mSourceSeqNum(sequenceNumberProtected.begin(), sequenceNumberProtected.end()) {\n}\n\nFecPacketsConnection::FecPacketsConnection() {\n}\n\nvoid FecPacketsConnection::addRowRepair(const uint16_t repairSeqNum,\n                                        const std::vector<uint16_t> sequenceNumberProtected) {\n\tmRowRepairNodes.emplace(repairSeqNum, FecRepairNode(sequenceNumberProtected));\n\tfor (uint16_t seqNum : sequenceNumberProtected) {\n\t\tif (mSourceNodes.count(seqNum) == 0) {\n\t\t\tmSourceNodes.emplace(seqNum, FecSourceNode(seqNum));\n\t\t}\n\t\tmSourceNodes.at(seqNum).addRowRepair(repairSeqNum);\n\t}\n}\n\nvoid FecPacketsConnection::addColRepair(const uint16_t repairSeqNum,\n                                        const std::vector<uint16_t> sequenceNumberProtected) {\n\tmColRepairNodes.emplace(repairSeqNum, FecRepairNode(sequenceNumberProtected));\n\tfor (uint16_t seqNum : sequenceNumberProtected) {\n\t\tif (mSourceNodes.count(seqNum) == 0) {\n\t\t\tmSourceNodes.emplace(seqNum, FecSourceNode(seqNum));\n\t\t}\n\t\tmSourceNodes.at(seqNum).addColRepair(repairSeqNum);\n\t}\n}\n\nvoid FecPacketsConnection::getRepairPacketsToRecoverSource(const uint16_t seqNum,\n                                                           std::set<uint16_t> &rowRepairPackets,\n                                                           std::set<uint16_t> &colRepairPackets) const {\n\n\trowRepairPackets.clear();\n\tcolRepairPackets.clear();\n\tif (mSourceNodes.count(seqNum) == 0) {\n\t\treturn;\n\t}\n\n\tstd::set<uint16_t> exploredSources = {};\n\tstd::set<uint16_t> addedSources = {seqNum};\n\tstd::set<uint16_t> addedRowRepair = {};\n\tstd::set<uint16_t> addedColRepair = {};\n\tfor (int it = 0; it < 10; it++) {\n\n\t\taddedRowRepair.clear();\n\t\taddedColRepair.clear();\n\t\tfor (const uint16_t protectedSource : addedSources) {\n\t\t\tif (mSourceNodes.count(protectedSource) == 1) {\n\t\t\t\tfor (const uint16_t newRepair : mSourceNodes.at(protectedSource).getRowRepair()) {\n\t\t\t\t\tif (rowRepairPackets.count(newRepair) == 0) {\n\t\t\t\t\t\taddedRowRepair.insert(newRepair);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const uint16_t newRepair : mSourceNodes.at(protectedSource).getColRepair()) {\n\t\t\t\t\tif (colRepairPackets.count(newRepair) == 0) {\n\t\t\t\t\t\taddedColRepair.insert(newRepair);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const uint16_t newSource : addedSources) {\n\t\t\texploredSources.insert(newSource);\n\t\t}\n\t\taddedSources.clear();\n\t\tfor (const uint16_t newRepair : addedRowRepair) {\n\t\t\tfor (const uint16_t newSource : mRowRepairNodes.at(newRepair).getProtectedSources()) {\n\t\t\t\tif (exploredSources.count(newSource) == 0 && mSourceNodes.count(newSource) == 1) {\n\t\t\t\t\taddedSources.insert(newSource);\n\t\t\t\t}\n\t\t\t}\n\t\t\trowRepairPackets.insert(newRepair);\n\t\t}\n\t\tfor (const uint16_t newRepair : addedColRepair) {\n\t\t\tfor (const uint16_t newSource : mColRepairNodes.at(newRepair).getProtectedSources()) {\n\t\t\t\tif (exploredSources.count(newSource) == 0 && mSourceNodes.count(newSource) == 1) {\n\t\t\t\t\taddedSources.insert(newSource);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcolRepairPackets.insert(newRepair);\n\t\t}\n\n\t\tif (addedSources.size() == 0) {\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid FecPacketsConnection::cleanRowRepair(const uint16_t seqNum) {\n\tif (mRowRepairNodes.count(seqNum) == 0) {\n\t\treturn;\n\t}\n\n\tfor (auto source : mRowRepairNodes.at(seqNum).getProtectedSources()) {\n\t\tif (mSourceNodes.at(source).removeRowRepair(seqNum)) {\n\t\t\tmSourceNodes.erase(source);\n\t\t}\n\t}\n\tmRowRepairNodes.erase(seqNum);\n}\n\nvoid FecPacketsConnection::cleanColRepair(const uint16_t seqNum) {\n\tif (mColRepairNodes.count(seqNum) == 0) {\n\t\treturn;\n\t}\n\n\tfor (auto source : mColRepairNodes.at(seqNum).getProtectedSources()) {\n\t\tif (mSourceNodes.at(source).removeColRepair(seqNum)) {\n\t\t\tmSourceNodes.erase(source);\n\t\t}\n\t}\n\tmColRepairNodes.erase(seqNum);\n}\n\nvoid FecPacketsConnection::reset() {\n\tmSourceNodes.clear();\n\tmRowRepairNodes.clear();\n\tmColRepairNodes.clear();\n}"
  },
  {
    "path": "src/fecstream/fec-packets-connection.h",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef FEC_PACKETS_CONNECTION_H\n#define FEC_PACKETS_CONNECTION_H\n\n#include <map>\n#include <memory>\n#include <set>\n\n#include \"packet-api.h\"\n\nnamespace ortp {\n\n#ifdef _WIN32\n// Disable C4251 triggered by need to export all stl template classes\n#pragma warning(disable : 4251)\n#endif // ifdef _WIN32\n\n/** @class FecSourceNode\n * @brief Class to connect a single source packet with the repair packets that protect it.\n *\n * The source packet is identified by its sequence number mSeqNum. The repair packets are identified by their sequence\n * number, with two possibilities:\n * - for row repair packets, the sequence number is added to mRowRepairSeqNum,\n * - for column repair packets, the sequence number is added to mColRepairSeqNum.\n */\nclass ORTP_PUBLIC FecSourceNode {\npublic:\n\t/**\n\t * This constructor initializes the object with the sequence number of the source packet.\n\t *\n\t * @param seqNum Sequence number of the source packet.\n\t */\n\tFecSourceNode(const uint16_t seqNum);\n\n\t~FecSourceNode(){};\n\n\t/**\n\t * Connect the source packet with a row repair packet that protects it by adding its sequence number in\n\t * mRowRepairSeqNum.\n\t *\n\t * @param seqNum sequence number of the row repair packet.\n\t */\n\tvoid addRowRepair(const uint16_t seqNum);\n\n\t/**\n\t * Connect the source packet with a column repair packet that protects it by adding its sequence number in\n\t * mColRepairSeqNum.\n\t *\n\t * @param seqNum sequence number of the column repair packet.\n\t */\n\tvoid addColRepair(const uint16_t seqNum);\n\n\t/**\n\t * Return the set of sequence numbers of the row repair packets that protects the source packet.\n\t */\n\tstd::set<uint16_t> getRowRepair() const {\n\t\treturn mRowRepairSeqNum;\n\t};\n\n\t/**\n\t * Return the set of sequence numbers of the column repair packets that protects the source packet.\n\t */\n\tstd::set<uint16_t> getColRepair() const {\n\t\treturn mColRepairSeqNum;\n\t};\n\n\t/**\n\t * Return the sequence number of the source packet.\n\t */\n\tuint16_t getSeqNum() const {\n\t\treturn mSeqNum;\n\t};\n\n\t/**\n\t * Remove the connection with the row repair packet identified by repairSeqNum. Return true if the node has no more\n\t * connections.\n\t *\n\t * @param repairSeqNum sequence number of the row repair packet.\n\t * @return true if the source packet is no more connected with a repair packet.\n\t */\n\tbool removeRowRepair(const uint16_t repairSeqNum);\n\n\t/**\n\t * Remove the connection with the column repair packet identified by repairSeqNum. Return true if the node has no\n\t * more connections.\n\t *\n\t * @param repairSeqNum sequence number of the column repair packet.\n\t * @return true if the source packet is no more connected with a repair packet.\n\t */\n\tbool removeColRepair(const uint16_t repairSeqNum);\n\nprivate:\n\tconst uint16_t mSeqNum; /**< Sequence number of the source packet. */\n\n\tstd::set<uint16_t> mRowRepairSeqNum =\n\t    {}; /**< Set of sequence numbers of the row repair packets that protect the source packet. */\n\n\tstd::set<uint16_t> mColRepairSeqNum =\n\t    {}; /**< Set of sequence numbers of the column repair packets that protect the source packet. */\n};\n\n/** @class FecRepairNode\n * @brief Class to connect a single repair packet with the source packets that it protects.\n *\n * The packets are identified by their unique sequence numbers.\n */\nclass ORTP_PUBLIC FecRepairNode {\npublic:\n\t/**\n\t * This constructor initializes the object with the sequence number of the repair packet and the list of the\n\t * source packets that it protects, given by their sequence numbers.\n\t *\n\t * @param sequenceNumberProtected List of the sequence numbers of the protected source packets.\n\t */\n\tFecRepairNode(const std::vector<uint16_t> sequenceNumberProtected);\n\n\t~FecRepairNode(){};\n\n\t/**\n\t * Return the set of sequence numbers of the source packets that are protected by the repair packet.\n\t */\n\tstd::set<uint16_t> getProtectedSources() const {\n\t\treturn mSourceSeqNum;\n\t};\n\nprivate:\n\tconst std::set<uint16_t> mSourceSeqNum; /**< Set of sequence numbers of the source packets protected. */\n};\n\n/** @class FecPacketsConnection\n * @brief Class to connect the repair packets with the source packets that they protect.\n *\n * This class aims to establish the connections between the source and repair packets received when there is a\n * protection, in order to find the subset of repair packets needed to repair a given source packet. It can be\n * represented by a bipartite graph, whose nodes are the set of source packets on one side and the set of repair packets\n * of the other side. When a repair packet protects a source packet, both are connected. A FEC block can be fully\n * represented by such graph. If there is no overlap between two FEC blocks (no source packet in common), their graphs\n * are disjoint.\n *\n * The packets are identified by their sequence numbers. The row and column repair packets are distinguished. A source\n * packet has at least one connection. If the FEC blocks are disjoint, they can have at most two connections: one with a\n * row repair packet and the other with a column repair packet. The repair packets can protects several source packets.\n * The sets of packets are populated only when a repair packet is added, with the list of source packets that it\n * protects.\n *\n * To find the repair packets that protects the source packet i :\n *  1. the sets of row and columns repair packets connected to i are found\n *  2. then for each repair packet in those sets the source packets connected are found.\n * These steps are repeated until no more packets are found. This way any configuration of parity protection of the\n * flexible FEC can be handled, even with repair packet losses or when they are not received in chronological order.\n *\n * The sets are cleaned by removing a given repair packet and the connections that it had with the source packets.\n */\nclass ORTP_PUBLIC FecPacketsConnection {\npublic:\n\t/**\n\t * This constructor initializes the object.\n\t */\n\tFecPacketsConnection();\n\n\t~FecPacketsConnection(){};\n\n\t/**\n\t * @brief Add a row repair packet and the source packets that it protects.\n\t *\n\t * A FecRepairNode is created for the row repair packet and the sources packets that it protects. It is added to\n\t * the map mRowRepairNodes. A FecSourceNode is created for each protected source packet and added to the map\n\t * mSourceNodes. If the packets are already in the maps, the new connections are added.\n\t *\n\t * @param repairSeqNum Sequence number of the row repair packet.\n\t * @param sequenceNumberProtected List of sequence numbers of the source packets that are protected by the repair\n\t * packet.\n\t */\n\tvoid addRowRepair(const uint16_t repairSeqNum, const std::vector<uint16_t> sequenceNumberProtected);\n\n\t/**\n\t * @brief Add a column repair packet and the source packets that it protects.\n\t *\n\t * A FecRepairNode is created for the column repair packet and the sources packets that it protects. It is added to\n\t * the map mColRepairNodes. A FecSourceNode is created for each protected source packet and added to the map\n\t * mSourceNodes. If the packets are already in the maps, the new connections are added.\n\t *\n\t * @param repairSeqNum Sequence number of the column repair packet.\n\t * @param sequenceNumberProtected List of sequence numbers of the source packets that are protected by the repair\n\t * packet.\n\t */\n\tvoid addColRepair(const uint16_t repairSeqNum, const std::vector<uint16_t> sequenceNumberProtected);\n\n\t/**\n\t * @brief Identify all repair packets needed to recover the source packet seqNum.\n\t *\n\t * This function explores the full graph to which the source packet seqNum belongs and selects the row and column\n\t * repair packets that are vertices of this graph, as the graph is a representation of a FEC block. The operation\n\t * consists in the two steps:\n\t *  1. for each source packet of the graph, the sets of row and columns repair packets connected are found\n\t *  2. for each repair packet of the graph, the set of the source packets connected are found.\n\t * These steps are repeated until no more packets are found. It starts with the set {seqNum}.\n\t *\n\t * For the case where several FEC blocks overlap, the search could lead to a very large set of repair packetss.\n\t * However the source packets far from seqNum are unlikely to be essential to the recovery and the exploring cost\n\t * increases. The packet exploration is then limited to a given number of iterations.\n\t *\n\t * @param seqNum Sequence number of the source packet to recover.\n\t * @param rowRepairPackets Set of sequence numbers of the row repair packets needed to recover the source\n\t * packet.\n\t * @param colRepairPackets Set of sequence numbers of the column repair packets needed to recover the source\n\t * packet.\n\t */\n\tvoid getRepairPacketsToRecoverSource(const uint16_t seqNum,\n\t                                     std::set<uint16_t> &rowRepairPackets,\n\t                                     std::set<uint16_t> &colRepairPackets) const;\n\n\t/**\n\t * @brief Erase a row repair packet and its connections with the source packets.\n\t *\n\t * For each source packet connected with the row repair packet seqNum, remove the connection with it. If a source\n\t * packet has no more connections, erase it. Then erase the row repair packet.\n\t *\n\t * @param seqNum Sequence number of the row repair packet to remove.\n\t */\n\tvoid cleanRowRepair(const uint16_t seqNum);\n\n\t/**\n\t * @brief Erase a column repair packet and its connections with the source packets.\n\t *\n\t * For each source packet connected with the column repair packet seqNum, remove the connection with it. If a source\n\t * packet has no more connections, erase it. Then erase the column repair packet.\n\t *\n\t * @param seqNum Sequence number of the column repair packet to remove.\n\t */\n\tvoid cleanColRepair(const uint16_t seqNum);\n\n\t/**\n\t * Clear all packets and connections.\n\t */\n\tvoid reset();\n\nprivate:\n\tstd::map<uint16_t, FecSourceNode> mSourceNodes =\n\t    {}; /**< All source packets, identified by the sequence numbers, and their connections with repair packets. */\n\n\tstd::map<uint16_t, FecRepairNode> mRowRepairNodes = {}; /**< All row repair packets, identified by a unique sequence\n\t                                                           number, and their connections with source packets. */\n\n\tstd::map<uint16_t, FecRepairNode> mColRepairNodes = {}; /**< All column repair packets, identified by a unique\n\t           sequence number, and their connections with source packets. */\n};\n\n} // namespace ortp\n#endif // FEC_PACKETS_CONNECTION_H"
  },
  {
    "path": "src/fecstream/fec-params.cpp",
    "content": "#include \"fec-params.h\"\n#include \"ortp/logging.h\"\n\n/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\nusing namespace ortp;\n\nextern \"C\" FecParams *fec_params_new(uint32_t repairWindow) {\n\treturn (FecParams *)(new FecParamsController(repairWindow));\n}\nextern \"C\" void fec_params_destroy(FecParams *params) {\n\tdelete (FecParamsController *)params;\n}\nextern \"C\" void fec_params_update(FecParams *params, uint8_t level) {\n\treturn ((FecParamsController *)params)->updateParams(level);\n}\nextern \"C\" uint8_t fec_params_estimate_best_level(\n    FecParams *params, float lossRate, int bitrate, float currentOverhead, float *estimatedOverhead) {\n\treturn ((FecParamsController *)params)->estimateBestLevel(lossRate, bitrate, currentOverhead, estimatedOverhead);\n}\n\nFecParamsController::FecParamsController(uint32_t repairWindow) : mRepairWindow(repairWindow), mEnabled(false) {\n\tmL = 0;\n\tmD = 0;\n\tmIs2D = false;\n\tmLevel = 0;\n\tmOverhead = 0.;\n\tcomputeLossRateTables();\n}\n\nvoid FecParamsController::addSubscriber(FecParamsSubscriber *subscriber) {\n\tmSubscribers.push_back(subscriber);\n}\n\nvoid FecParamsController::notifySubscribers() {\n\tfor (auto &subscriber : mSubscribers) {\n\t\tsubscriber->update(this);\n\t}\n}\n\nvoid FecParamsController::removeSubscriber(FecParamsSubscriber *subscriber) {\n\n\tfor (auto it = mSubscribers.begin(); it != mSubscribers.end(); it++) {\n\t\tif (*it == subscriber) {\n\t\t\tmSubscribers.erase(it);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid FecParamsController::computeLossRateTableGivenOverhead(\n    std::array<float, 5> *limits, float p0x, float p0y, float p1x, float p1y) {\n\tfloat a = (p1y - p0y) / (p1x - p0x);\n\tfloat b = p0y - a * p0x;\n\tfor (size_t i = 0; i < mOverheadLimits.size() - 1; i++) {\n\t\tlimits->at(i) = (mOverheadLimits.at(i + 1) - b) / a;\n\t}\n}\n\nvoid FecParamsController::computeLossRateTables() {\n\tcomputeLossRateTableGivenOverhead(&mLossRateLimitsLowBandwidth, 3.f, mOverheadLimits.at(1), 8.f,\n\t                                  mOverheadLimits.at(3));\n\tcomputeLossRateTableGivenOverhead(&mLossRateLimitsMediumBandwidth, 0.5f, mOverheadLimits.at(1), 5.f,\n\t                                  mOverheadLimits.at(5));\n\tcomputeLossRateTableGivenOverhead(&mLossRateLimitsHighBandwidth, 0.0f, mOverheadLimits.at(1), 1.f,\n\t                                  mOverheadLimits.at(5));\n}\n\nfloat FecParamsController::computeOverhead(uint8_t L, uint8_t D, bool is2D) {\n\tif (L == 0) return 0.f;\n\tif (is2D) {\n\t\treturn (1.0f / (float)L) + ((D > 0) ? (1.0f / (float)D) : 0.0f);\n\t} else if (D == 0) {\n\t\treturn 1.0f / (float)L;\n\t} else {\n\t\treturn 1.0f / (float)D;\n\t}\n}\n\nstd::array<float, 5> FecParamsController::findBandwidthRange(int availableBandwidth) {\n\tif (availableBandwidth < mLowBandwidth) {\n\t\tortp_message(\"[flexfec] [%p] available bandwidth: %d (low)\", this, availableBandwidth);\n\t\tmMaximalOverhead = mMaxOverheadList.at(0);\n\t\treturn mLossRateLimitsLowBandwidth;\n\t} else if (availableBandwidth > mHighBandwidth) {\n\t\tortp_message(\"[flexfec] [%p] available bandwidth: %d (high)\", this, availableBandwidth);\n\t\tmMaximalOverhead = mMaxOverheadList.at(2);\n\t\treturn mLossRateLimitsHighBandwidth;\n\t} else {\n\t\tortp_message(\"[flexfec] [%p] available bandwidth: %d (medium)\", this, availableBandwidth);\n\t\tmMaximalOverhead = mMaxOverheadList.at(1);\n\t\treturn mLossRateLimitsMediumBandwidth;\n\t}\n}\n\nuint8_t FecParamsController::findLevelGivenLossRate(float lossRate, std::array<float, 5> *lossRateLimits) {\n\tuint8_t i = static_cast<uint8_t>(lossRateLimits->size());\n\tbool paramFound = false;\n\twhile (!paramFound && i > 0) {\n\t\tif (lossRate >= lossRateLimits->at(i - 1)) {\n\t\t\treturn i;\n\t\t}\n\t\ti--;\n\t}\n\treturn 0;\n}\n\nuint8_t FecParamsController::estimateBestLevel(float lossRate,\n                                               int availableBandwidth,\n                                               float currentOverhead,\n                                               float *estimatedOverhead) {\n\n\tif (lossRate > mMaxLossRate) {\n\t\tortp_message(\"[flexfec] [%p] high value of loss rate estimation (%f), probable congestion, \"\n\t\t             \"disable FEC. Current fec overhead %f.\",\n\t\t             this, lossRate, currentOverhead);\n\t\t*estimatedOverhead = 0.;\n\t\treturn 0;\n\t}\n\n\t// best fec level for given loss rate and current bandwidth\n\tauto lossRateLimits = findBandwidthRange(availableBandwidth);\n\tuint8_t bestLevel = findLevelGivenLossRate(lossRate, &lossRateLimits);\n\tfloat theoreticalOverhead =\n\t    computeOverhead(mLvalues.at(bestLevel), mDvalues.at(bestLevel), mIs2Dvalues.at(bestLevel));\n\tfloat newOverhead = 0.f;\n\n\tif (mOverhead > 0.) {\n\t\t// estimation of overhead for this level\n\t\tnewOverhead = theoreticalOverhead / mOverhead * currentOverhead;\n\n\t\t// reduction of the fec level if needed\n\t\twhile (newOverhead > mMaximalOverhead) {\n\t\t\tortp_message(\"[flexfec] [%p] reduce expected FEC level %d, because overhead is %f\", this, bestLevel,\n\t\t\t             newOverhead);\n\t\t\tbestLevel--;\n\t\t\ttheoreticalOverhead =\n\t\t\t    computeOverhead(mLvalues.at(bestLevel), mDvalues.at(bestLevel), mIs2Dvalues.at(bestLevel));\n\t\t\tnewOverhead = theoreticalOverhead / mOverhead * currentOverhead;\n\t\t}\n\t}\n\t// FEC is currently disabled\n\tif (newOverhead == 0.) {\n\t\tnewOverhead = theoreticalOverhead * 2.f;\n\n\t\t// reduction of the fec level if needed\n\t\twhile (newOverhead > mMaximalOverhead) {\n\t\t\tbestLevel--;\n\t\t\ttheoreticalOverhead =\n\t\t\t    computeOverhead(mLvalues.at(bestLevel), mDvalues.at(bestLevel), mIs2Dvalues.at(bestLevel));\n\t\t\tnewOverhead = theoreticalOverhead * 2.f;\n\t\t}\n\t}\n\t*estimatedOverhead = newOverhead;\n\tortp_message(\"[flexfec] [%p] proposed FEC level %d for L = %d, D = %d, estimated overhead %f (< %f)\"\n\t             \" for loss rate %f, current fec overhead %f\",\n\t             this, bestLevel, mLvalues.at(bestLevel), mDvalues.at(bestLevel), *estimatedOverhead, mMaximalOverhead,\n\t             lossRate, currentOverhead);\n\treturn bestLevel;\n}\n\nvoid FecParamsController::updateParams(uint8_t level) {\n\tif (level >= mLvalues.size()) {\n\t\tortp_message(\"[flexfec] [%p] Can't change parameters, FEC level %u doesn't exist.\", this, level);\n\t\treturn;\n\t}\n\tif (mLevel != level) {\n\t\tmLevel = level;\n\t\tif (mLevel == 0) {\n\t\t\tdisable();\n\t\t} else {\n\t\t\tenable();\n\t\t\tmD = mDvalues.at(mLevel);\n\t\t\tmL = mLvalues.at(mLevel);\n\t\t\tmIs2D = mIs2Dvalues.at(mLevel);\n\t\t}\n\t\tortp_message(\"[flexfec] [%p] Parameters changed : L = %u and D = %u, %s, level %d\", this, mL, mD,\n\t\t             mIs2D ? \"2D\" : \"1D\", mLevel);\n\t\tmOverhead = computeOverhead(mL, mD, mIs2D);\n\t\tnotifySubscribers();\n\t}\n}\n\nvoid FecParamsController::enable() {\n\tif (mEnabled) return;\n\tmEnabled = true;\n\tortp_message(\"[flexfec] [%p] Enabling flexfec ...\", this);\n}\n\nvoid FecParamsController::disable() {\n\tif (!mEnabled) return;\n\tmEnabled = false;\n\tmL = 0;\n\tmD = 0;\n\tmIs2D = false;\n\tmOverhead = 0.f;\n\tmLevel = 0;\n\tortp_message(\"[flexfec] [%p] Disabling flexfec\", this);\n}\n\nFecParamsSubscriber::~FecParamsSubscriber() {\n}"
  },
  {
    "path": "src/fecstream/fec-params.h",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef FEC_PARAMS_H\n#define FEC_PARAMS_H\n\n#include <array>\n#include <ortp/rtpsession.h>\n#include <vector>\n\nnamespace ortp {\n\n#ifdef _WIN32\n// Disable C4251 triggered by need to export all stl template classes\n#pragma warning(disable : 4251)\n#endif // ifdef _WIN32\n\nclass FecParamsController;\n\n/** @class FecParamsSubscriber\n * @brief Class to update the FecParamsController.\n */\nclass ORTP_PUBLIC FecParamsSubscriber {\npublic:\n\tvirtual void update(FecParamsController *) = 0;\n\tvirtual ~FecParamsSubscriber();\n};\n\n/** @class FecParamsController\n * @brief Class to control the parameters of the FEC.\n *\n * This class set the value of the repair window, and the parameters of the parity protection. A given FEC protection\n * configuration is represented by a FEC block with D rows and L colmuns, as described in RFC 8627. The configuration\n * can be:\n * - 1D non interleaved parity protection or row protection: L successive source packets are combined together on the\n * same row, and 1 row repair packet is generated.\n * - 1D interleaved parity protection, or column protection: D source packets are combined together every L packets, on\n * the same column, and 1 column repair packet is generated per column. It leads to L column repair packets for L\n * independent columns, for a set of L*D source packets in the FEC block.\n * - 2D parity protection : both row and column protection are applied, and D row repair packets and L column repair\n * packets are generated.\n *\n * Several levels of FEC protection have been defined, that give an increasing recovery rate but at the cost of an\n * increasing redundancy. The controller can identify the optimal level depending on the values of the loss rate, the\n * available bandwidth and the part of the bandwidth that is dedicated to the FEC repair packets, that is given by the\n * overhead. In order to limit the use of bandwidth by the FEC when the total available bandwidth is limited, several\n * rules have been defined between the loss rate and the FEC level, given the bandwidth. The protection increases\n * slowly with the loss rate in case of low bandwidth, and rapidly in case of high bandwidth.\n *\n * Each time that the FEC level is updated, the subscribers are notified and they receive the new FEC parameters.\n *\n * The bandwidths values are given in bit/s.\n */\nclass ORTP_PUBLIC FecParamsController {\npublic:\n\t/**\n\t * @brief Initialize the FEC parameters controller.\n\t *\n\t * Set the value of the repair window, the FEC to disabled and compute the table of loss rate values given\n\t * bandwidth.\n\t *\n\t * @param repairWindow Duration of the repair window, in micro seconds.\n\t */\n\tFecParamsController(uint32_t repairWindow);\n\n\t/**\n\t * @brief Update the FEC parameters for a given FEC protection level and notify subscribers.\n\t *\n\t * The subscribers are notified only if the new level is not equal to the current one. If not, the new values of mL,\n\t * mD and mIs2D are set, and the state mEnabled is updated. The new value of mOverhead is computed.\n\t *\n\t * @param level FEC protection level.\n\t */\n\tvoid updateParams(uint8_t level);\n\n\t/**\n\t * @brief Estimate the optimal FEC protection level for given values of loss rate, total available bandwidth and\n\t * current overhead.\n\t *\n\t * At first the available bandwidth is analyzed to select the right table of loss rates between the low, medium and\n\t * high bandwidth cases. It gives the relationship between the loss rate and the FEC level, related to the current\n\t * state of the network. Then the FEC level is determined by finding the loss rate interval that contains the\n\t * current loss rate. The related overhead is estimated from the current overhead, measured by the real ratio\n\t * between the repair and source sizes, and the theoretical overhead with the new FEC level. It allows to take into\n\t * account the average size of the packets. This overhead is an estimate and may be different from the reality,\n\t * because the size of the packets is not constant during the call and depends on the video (or audio) encoder\n\t * parameters. If the estimated overhead is over a limit value, the FEC will consume too much bandwidth. In that\n\t * case, the proposed FEC level is reduced. This check is repeated until the estimated overhead falls below the\n\t * limit.\n\t *\n\t * If the FEC is not currently enabled, there is no measurement of the sizes of the source and the repair packets\n\t * trough the overhead. Then the value of the theoretical overhead is taken by default, with the hypothesis that the\n\t * source and the repair packets have the same size. As it has been observed that it underestimates the real\n\t * overhead, a correction factor is applied.\n\t *\n\t * If the loss rate is greater than mMaxLossRate, there might be a congestion. In that case, the FEC level returned\n\t * is 0.\n\t *\n\t * @param lossRate Current loss rate: ratio between the number of packets received and packets send.\n\t * @param availableBandwidth Total available bandwidth, for the source stream and the FEC stream, in bit/s.\n\t * @param currentOverhead Measurement of the current overhead: ratio between the size of the repair packets sent\n\t * over the size of the source packets sent.\n\t * @param estimatedOverhead To return the estimation of the new overhead for the estimated best FEC level.\n\t * @return Optimal FEC protection level estimated.\n\t */\n\tuint8_t estimateBestLevel(float lossRate, int availableBandwidth, float currentOverhead, float *estimatedOverhead);\n\n\t/**\n\t * @brief Add a subscriber.\n\t */\n\tvoid addSubscriber(FecParamsSubscriber *subscriber);\n\n\t/**\n\t * @brief Remove a subscriber.\n\t */\n\tvoid removeSubscriber(FecParamsSubscriber *subscriber);\n\n\t/**\n\t * @brief Return the current FEC parameter mL that gives the number of columns of a FEC block.\n\t */\n\tuint8_t getL() {\n\t\treturn mL;\n\t}\n\n\t/**\n\t * @brief Return the current FEC parameter mD that gives the number of rows of a FEC block.\n\t */\n\tuint8_t getD() {\n\t\treturn mD;\n\t}\n\n\t/**\n\t * @brief Return true if the current FEC configuration is a 2D parity protection, false otherwise.\n\t */\n\tbool is2D() {\n\t\treturn mIs2D;\n\t}\n\n\t/**\n\t * @brief Return true if the FEC is currently enabled, false otherwise.\n\t */\n\tbool getEnabled() {\n\t\treturn mEnabled;\n\t}\n\n\t/**\n\t * @brief Return the duration of the repair window, in micro seconds.\n\t */\n\tuint32_t getRepairWindow() {\n\t\treturn mRepairWindow;\n\t}\n\n\t~FecParamsController(){};\n\nprivate:\n\t/**\n\t * @brief Notify the subscribers for the current set of FEC parameters.\n\t */\n\tvoid notifySubscribers();\n\n\t/**\n\t * @brief Set the parameter mEnabled to true.\n\t */\n\tvoid enable();\n\n\t/**\n\t * @brief Set the parameter mEnabled to false, and the FEC level and other parameters to 0.\n\t */\n\tvoid disable();\n\n\t/**\n\t * @brief Compute the table of the loss rates related to the values of overhead in mOverheadLimits.\n\t *\n\t * The loss rate values are computed from the overhead values such that the overhead = f(loss rate) where f is the\n\t * line that passes through points P0(p0x, p0y) and P1(p1x, p1y).\n\t *\n\t * @param limits Table of loss rate limits computed.\n\t * @param p0x Loss rate value of P0.\n\t * @param p0y Overhead value of P0.\n\t * @param p1x Loss rate value of P1.\n\t * @param p1y Overhead value of P1.\n\t */\n\tvoid computeLossRateTableGivenOverhead(std::array<float, 5> *limits, float p0x, float p0y, float p1x, float p1y);\n\n\t/**\n\t * @brief Compute the tables of loss rates to identify the FEC level to apply given a loss rate for the three cases:\n\t * low, medium and high available bandwidth.\n\t *\n\t * The tables mLossRateLimitsLowBandwidth, mLossRateLimitsMediumBandwidth and mLossRateLimitsHighBandwidth are\n\t * computed and give the intervals of loss rate where a given FEC level is applied.\n\t */\n\tvoid computeLossRateTables();\n\n\t/**\n\t * @brief Compute the theoretical overhead if the source packets and the repair packet have the same size, for a\n\t * given FEC protection configuration.\n\t *\n\t * The overhead computation depends on the FEC protection:\n\t * - in 1D non interleaved, overhead = 1/L\n\t * - in 1D interleaved, overhead = 1/D\n\t * - in 2D, overhead = 1/L + 1/D.\n\t *\n\t * @param L Number of columns in a FEC block, not taken into account if D > 1 and is2D is false.\n\t * @param D Number of rows in a FEC block.\n\t * @param is2D True for 2D parity protection, false otherwise.\n\t * @return The overhead computed.\n\t */\n\tfloat computeOverhead(uint8_t L, uint8_t D, bool is2D);\n\n\t/**\n\t * @brief Return the table of loss rates to use for the given value of available bandwidth and set the maximal\n\t * overhead value mMaximalOverhead accordingly.\n\t *\n\t * @param availableBandwidth Available bandwidth, in micro seconds.\n\t * @return Table of loss rates.\n\t */\n\tstd::array<float, 5> findBandwidthRange(int availableBandwidth);\n\n\t/**\n\t * @brief Return the index of the interval that contains lossRate in the table lossRateLimits. Return 0 if the loss\n\t * rate is smaller than the smallest value in the table.\n\t *\n\t * @param lossRate Loss rate value to compare to the table.\n\t * @param lossRateLimits Table of loss rates.\n\t * @return Index of the interval that contains lossRate.\n\t */\n\tuint8_t findLevelGivenLossRate(float lossRate, std::array<float, 5> *lossRateLimits);\n\n\tstd::vector<FecParamsSubscriber *> mSubscribers; /**< Subscribers to the FEC parameter controller.*/\n\tuint32_t mRepairWindow;                          /**< Duration of the repair window, in micro seconds.*/\n\tuint8_t mLevel;  /**< Current FEC level, from 0 (disabled) to mLvalues.size(). The other current parameters are set\n\t                    thanks to this index.*/\n\tbool mEnabled;   /**< True to enable the FEC, false otherwise.*/\n\tuint8_t mL;      /**< Number of columns L of a FEC block. Its current value is mLvalues.at(mLevel).*/\n\tuint8_t mD;      /**< Number of rows D of a FEC block. Its current value is mDvalues.at(mLevel).*/\n\tbool mIs2D;      /**< True if the FEC parity protection applies in 2D, false otherwise. Its current value is\n\t                    mIs2Dvalues.at(mLevel).*/\n\tfloat mOverhead; /**< Theoretical overhead, if the source and repair packets have the same size, for the current FEC\n\t                    parameters. 0 if the FEC is disabled. Its current value is mLvalues.at(mLevel).*/\n\tfloat mMaximalOverhead = 0.8f; /**< Maximal overhead allowed to not use too much bandwidth for FEC stream. It is\n\t                                 updated given the current available bandwidth by the function findBandwidthRange.*/\n\tconst std::array<float, 3> mMaxOverheadList = {\n\t    0.5f, 0.7f, 0.9f}; /**< List of maximal overheads allowed given the available bandwidth, from low to high.*/\n\tstd::array<float, 5> mLossRateLimitsLowBandwidth; /**< Table of loss rate given the FEC level from low to high, in\n\t                                                     case of low bandwidth.*/\n\tstd::array<float, 5> mLossRateLimitsMediumBandwidth; /**< Table of loss rate given the FEC level from low to high,\n\t                                                        in case of medium bandwidth.*/\n\tstd::array<float, 5> mLossRateLimitsHighBandwidth; /**< Table of loss rate given the FEC level from low to high, in\n\t                                                      case of high bandwidth.*/\n\tconst float mMaxLossRate =\n\t    20.f; /**< Maximal loss rate taken to compute the FEC level. If the loss rate is greater\n\t            than this value, the FEC should be disabled, because there might be a congestion.*/\n\tint const mLowBandwidth =\n\t    100000; /**< Low bandwidth threshold: below that value, the FEC is limited to avoid congestion.*/\n\tint const mHighBandwidth = 300000; /**< High bandwidth threshold: above that value, the FEC level increases quickly\n\t                                      with the loss rate.*/\n\tstd::array<float, 6> const mOverheadLimits = {\n\t    0.f,  0.1f,     0.2f, 0.4f,\n\t    0.5f, 2.f / 3.f}; /**< Theoretical overheads taken to compute the loss rate tables for an increasing FEC level.\n\t                         It is related to mLvalues, mDvalues and mIs2Dvalues.*/\n\tstd::array<uint8_t, 6> const mLvalues = {0, 10, 5, 5,\n\t                                         4, 3}; /**< Table of FEC parameter L for an increasing\n\t       FEC level. It is related to mOverheadLimits, mDvalues and mIs2Dvalues.*/\n\tstd::array<uint8_t, 6> const mDvalues = {0, 0, 5, 5,\n\t                                         4, 3}; /**< Table of FEC parameter D for an increasing\n\t       FEC level. It is related to mOverheadLimits, mLvalues and mIs2Dvalues.*/\n\tstd::array<bool, 6> const mIs2Dvalues = {false, false, false, true,\n\t                                         true,  true}; /**< Table of FEC parameter 1D or 2D for\nan increasing FEC level. It is related to mOverheadLimits, mLvalues and mDvalues.*/\n};\n} // namespace ortp\n#endif // FEC_PARAMS_H"
  },
  {
    "path": "src/fecstream/fec-stream-stats.cc",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <algorithm>\n#include <inttypes.h>\n\n#include \"fec-stream-stats.h\"\n#include \"ortp/logging.h\"\n\n#define HEADER_1 '\\x80'\n#define HEADER_2 '\\0'\n\nusing namespace ortp;\n\nFecStreamStats::FecStreamStats() {\n\tmemset(&mFecStats, 0, sizeof(fec_stats));\n\tmLocalHistoRecovering.resize(mBins);\n\tmLocalHistoLost.resize(mBins);\n\tmLocalHistoMissingGap.resize(mBins);\n\tmLocalHistoGapSize.resize(mBins);\n\tmGlobalHistoRecovering.resize(mBins);\n\tmGlobalHistoLost.resize(mBins);\n\tmGlobalHistoMissingGap.resize(mBins);\n\tmGlobalHistoGapSize.resize(mBins);\n\tstd::fill(mLocalHistoRecovering.begin(), mLocalHistoRecovering.begin(), 0);\n\tstd::fill(mLocalHistoLost.begin(), mLocalHistoLost.end(), 0);\n\tstd::fill(mLocalHistoMissingGap.begin(), mLocalHistoMissingGap.end(), 0);\n\tstd::fill(mLocalHistoGapSize.begin(), mLocalHistoGapSize.end(), 0);\n\tstd::fill(mGlobalHistoRecovering.begin(), mGlobalHistoRecovering.end(), 0);\n\tstd::fill(mGlobalHistoLost.begin(), mGlobalHistoLost.end(), 0);\n\tstd::fill(mGlobalHistoMissingGap.begin(), mGlobalHistoMissingGap.end(), 0);\n\tstd::fill(mGlobalHistoGapSize.begin(), mGlobalHistoGapSize.end(), 0);\n}\n\nvoid FecStreamStats::askedPacket(uint16_t seqNum) {\n\tauto &count = mMissingPackets[seqNum];\n\tif (count == 0) count = 1;\n\telse ++count;\n\n\tif (mMissingPackets.size() >= mMaxSize) {\n\t\tprintHistoAndClear();\n\t}\n}\n\nvoid FecStreamStats::definitelyLostPacket(uint16_t newSeqNumReceived, int16_t diff) {\n\tif (diff > 0) {\n\t\tuint64_t lost_packets = static_cast<uint64_t>(diff);\n\t\tuint16_t lastSeqNumReceived = newSeqNumReceived - (uint16_t)diff - 1;\n\t\tif (diff > (int)mMaxSize) {\n\t\t\tortp_message(\n\t\t\t    \"[flexfec] too much packets (%d) lost between packets %u and %u, do not count all of them in FEC stats\",\n\t\t\t    diff, lastSeqNumReceived, newSeqNumReceived);\n\t\t\tlost_packets = 0;\n\t\t\tfor (auto missingPacket : mMissingPackets) {\n\t\t\t\tif ((missingPacket.first > lastSeqNumReceived) && (missingPacket.first < newSeqNumReceived)) {\n\t\t\t\t\tmLostPackets.emplace_back(missingPacket.first);\n\t\t\t\t\tlost_packets++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tprintHistoAndClear();\n\t\t} else {\n\t\t\tfor (uint16_t seqNumPrec = lastSeqNumReceived + 1; seqNumPrec < newSeqNumReceived; seqNumPrec++) {\n\t\t\t\tmLostPackets.emplace_back(seqNumPrec);\n\t\t\t}\n\t\t\tif (mLostPackets.size() >= mMaxSize) {\n\t\t\t\tprintHistoAndClear();\n\t\t\t}\n\t\t}\n\n\t\tmFecStats.packets_not_recovered += lost_packets;\n\t\tmFecStats.packets_lost = mFecStats.packets_not_recovered + mFecStats.packets_recovered;\n\t}\n}\n\nvoid FecStreamStats::repairedPacket(uint16_t seqNum) {\n\tmRepairedPackets.emplace_back(seqNum);\n\tmFecStats.packets_recovered++;\n\tmFecStats.packets_lost = mFecStats.packets_not_recovered + mFecStats.packets_recovered;\n}\n\nvoid FecStreamStats::printStats(RtpSession *sourceSession, RtpSession *fecSession) {\n\tdouble initialLossRate =\n\t    (sourceSession->stats.packet_recv == 0)\n\t        ? 0.\n\t        : static_cast<double>(mFecStats.packets_lost) / static_cast<double>(sourceSession->stats.packet_recv);\n\tdouble residualLossRate = (sourceSession->stats.packet_recv == 0)\n\t                              ? 0.\n\t                              : static_cast<double>(mFecStats.packets_lost - mFecStats.packets_recovered) /\n\t                                    static_cast<double>(sourceSession->stats.packet_recv);\n\tdouble recoveringRate = (mFecStats.packets_lost == 0) ? 0.\n\t                                                      : static_cast<double>(mFecStats.packets_recovered) /\n\t                                                            static_cast<double>(mFecStats.packets_lost);\n\tauto stats = rtp_session_get_stats(fecSession);\n\tdouble ratio_sent = (sourceSession->stats.sent == 0)\n\t                        ? 0.\n\t                        : static_cast<double>(stats->sent) / static_cast<double>(sourceSession->stats.sent);\n\tdouble ratio_recv = (sourceSession->stats.recv == 0)\n\t                        ? 0.\n\t                        : static_cast<double>(stats->recv) / static_cast<double>(sourceSession->stats.recv);\n\n\tortp_log(ORTP_MESSAGE, \"===========================================================\");\n\tortp_log(ORTP_MESSAGE, \"               Forward Error Correction Stats              \");\n\tortp_log(ORTP_MESSAGE, \"-----------------------------------------------------------\");\n\tortp_log(ORTP_MESSAGE, \"\trow repair sent             \t%10\" PRId64 \" packets\", mFecStats.row_repair_sent);\n\tortp_log(ORTP_MESSAGE, \"\trow repair received         \t%10\" PRId64 \" packets\", mFecStats.row_repair_received);\n\tortp_log(ORTP_MESSAGE, \"\tcol repair sent             \t%10\" PRId64 \" packets\", mFecStats.col_repair_sent);\n\tortp_log(ORTP_MESSAGE, \"\tcol repair received         \t%10\" PRId64 \" packets\", mFecStats.col_repair_received);\n\tortp_log(ORTP_MESSAGE, \"\tpackets lost                \t%10\" PRId64 \" packets\", mFecStats.packets_lost);\n\tortp_log(ORTP_MESSAGE, \"\tpackets recovered           \t%10\" PRId64 \" packets\", mFecStats.packets_recovered);\n\tortp_log(ORTP_MESSAGE, \"\tinitial loss rate           \t%10.3f\", initialLossRate);\n\tortp_log(ORTP_MESSAGE, \"\trecovering rate             \t%10.3f\", recoveringRate);\n\tortp_log(ORTP_MESSAGE, \"\tresidual loss rate          \t%10.3f\", residualLossRate);\n\tortp_log(ORTP_MESSAGE, \"\tratio repair/source sizes sent  %10.3f\", ratio_sent);\n\tortp_log(ORTP_MESSAGE, \"\tratio repair/source sizes recv  %10.3f\", ratio_recv);\n\tortp_log(ORTP_MESSAGE, \"===========================================================\");\n\n\tprintGlobalHistoAndClear();\n}\n\nvoid FecStreamStats::clearAll() {\n\tprintLostPacketsHisto();\n\tmLostPackets.clear();\n\tmRepairedPackets.clear();\n\tmMissingPackets.clear();\n}\n\nvoid FecStreamStats::printHistoAndClear() {\n\tprintLostPacketsHisto();\n\tmLostPackets.clear();\n\tmRepairedPackets.clear();\n}\n\nstd::string FecStreamStats::histoToString(const std::vector<uint8_t> &histo) const {\n\tstd::string histoStr;\n\tfor (auto it = histo.begin(); it != histo.end(); ++it) {\n\t\thistoStr += std::to_string(*it);\n\t\thistoStr += \",\";\n\t}\n\thistoStr.pop_back();\n\treturn histoStr;\n}\n\nvoid FecStreamStats::printGlobalHistoAndClear() {\n\tprintLostPacketsHisto();\n\tortp_message(\"[flexfec] global histogram of successful repair attempts: %s\",\n\t             histoToString(mGlobalHistoRecovering).c_str());\n\tortp_message(\"[flexfec] global histogram of failed repair attempts: %s\", histoToString(mGlobalHistoLost).c_str());\n\tortp_message(\"[flexfec] global histogram of gaps between missing packets: %s\",\n\t             histoToString(mGlobalHistoMissingGap).c_str());\n\tortp_message(\"[flexfec] global histogram of number of consecutive packets loss: %s\",\n\t             histoToString(mGlobalHistoGapSize).c_str());\n\tmLostPackets.clear();\n\tmRepairedPackets.clear();\n\tmMissingPackets.clear();\n}\n\nvoid FecStreamStats::printLostPacketsHisto() {\n\n\tif (mMissingPackets.empty()) return;\n\n\tstd::vector<uint16_t> repairedPackets(mRepairedPackets.begin(), mRepairedPackets.end());\n\tstd::vector<uint16_t> lostPackets(mLostPackets.begin(), mLostPackets.end());\n\tstd::sort(repairedPackets.begin(), repairedPackets.end());\n\tstd::sort(lostPackets.begin(), lostPackets.end());\n\tstd::vector<uint16_t> mergedMissingPackets(repairedPackets.size() + lostPackets.size());\n\tstd::merge(repairedPackets.begin(), repairedPackets.end(), lostPackets.begin(), lostPackets.end(),\n\t           mergedMissingPackets.begin());\n\n\tint count_missing = (int)mergedMissingPackets.size();\n\tint count_repaired = (int)repairedPackets.size();\n\tint count_lost = (int)lostPackets.size();\n\tfloat recovery_rate = (count_missing == 0) ? 0.0f : (float)count_repaired / (float)count_missing;\n\tif (count_missing == 0 && mMissingPackets.size() >= mMaxSize) {\n\t\tortp_message(\"[flexfec] clear list of %d forgotten missing packets\", (int)mMissingPackets.size());\n\t\tmMissingPackets.clear();\n\t\treturn;\n\t}\n\tortp_message(\"[flexfec] local stats: %d packets missing, %d repaired, %d lost (recovery rate: %f)\", count_missing,\n\t             count_repaired, count_lost, recovery_rate);\n\n\tif (count_missing == 0) return;\n\n\tsize_t index = mBins - 1;\n\tfor (uint16_t seqNum : repairedPackets) {\n\t\tindex = (mMissingPackets[seqNum] < mBins - 1) ? mMissingPackets[seqNum] : mBins - 1;\n\t\t++mLocalHistoRecovering[index];\n\t\t++mGlobalHistoRecovering[index];\n\t\tmMissingPackets.erase(seqNum);\n\t}\n\tfor (uint16_t seqNum : lostPackets) {\n\t\tif (mMissingPackets.find(seqNum) == mMissingPackets.end()) {\n\t\t\t++mLocalHistoLost[0];\n\t\t\t++mGlobalHistoLost[0];\n\t\t} else {\n\t\t\tindex = (mMissingPackets[seqNum] < mBins - 1) ? mMissingPackets[seqNum] : mBins - 1;\n\t\t\t++mLocalHistoLost[index];\n\t\t\t++mGlobalHistoLost[index];\n\t\t\tmMissingPackets.erase(seqNum);\n\t\t}\n\t}\n\tsize_t lost_sequence_size = 1;\n\tif (mergedMissingPackets.size() > 1) {\n\t\tfor (size_t i = 0; i < mergedMissingPackets.size() - 1; i++) {\n\t\t\tuint16_t gap = mergedMissingPackets.at(i + 1) - mergedMissingPackets.at(i);\n\t\t\tindex = ((size_t)gap < mBins - 1) ? (size_t)gap : mBins - 1;\n\t\t\t++mLocalHistoMissingGap[index];\n\t\t\t++mGlobalHistoMissingGap[index];\n\t\t\tif (gap == 1) {\n\t\t\t\t++lost_sequence_size;\n\t\t\t} else {\n\t\t\t\tlost_sequence_size = (lost_sequence_size < mBins - 1) ? lost_sequence_size : mBins - 1;\n\t\t\t\t++mLocalHistoGapSize[lost_sequence_size];\n\t\t\t\t++mGlobalHistoGapSize[lost_sequence_size];\n\t\t\t\tlost_sequence_size = 1;\n\t\t\t}\n\t\t}\n\t\tlost_sequence_size = (lost_sequence_size < mBins - 1) ? lost_sequence_size : mBins - 1;\n\t\t++mLocalHistoGapSize[lost_sequence_size];\n\t\t++mGlobalHistoGapSize[lost_sequence_size];\n\t}\n\n\t// erase missing packets that are too old and where never repaired nor lost\n\t// this could occur because they arrived later in the jitter buffer\n\tif (count_lost > 0 && mMissingPackets.size() > 1) {\n\t\tuint16_t lastSeqNum = lostPackets.back();\n\t\tint count_forgotten = 0;\n\t\tfor (auto it = mMissingPackets.begin(); it != mMissingPackets.end();) {\n\t\t\tif (it->first <= lastSeqNum) {\n\t\t\t\tit = mMissingPackets.erase(it);\n\t\t\t\t++count_forgotten;\n\t\t\t} else {\n\t\t\t\t++it;\n\t\t\t}\n\t\t}\n\t\tif (count_forgotten > 0) ortp_message(\"[flexfec] clear list of %d old packets\", count_forgotten);\n\t}\n\n\tortp_message(\"[flexfec] local histogram of successful repair attempts: %s\",\n\t             histoToString(mLocalHistoRecovering).c_str());\n\tortp_message(\"[flexfec] local histogram of failed repair attempts: %s\", histoToString(mLocalHistoLost).c_str());\n\tortp_message(\"[flexfec] local histogram of gaps between missing packets: %s\",\n\t             histoToString(mLocalHistoMissingGap).c_str());\n\tortp_message(\"[flexfec] local histogram of number of consecutive packets loss: %s\",\n\t             histoToString(mLocalHistoGapSize).c_str());\n\n\tstd::fill(mLocalHistoRecovering.begin(), mLocalHistoRecovering.end(), 0);\n\tstd::fill(mLocalHistoLost.begin(), mLocalHistoLost.end(), 0);\n\tstd::fill(mLocalHistoMissingGap.begin(), mLocalHistoMissingGap.end(), 0);\n\tstd::fill(mLocalHistoGapSize.begin(), mLocalHistoGapSize.end(), 0);\n}\n"
  },
  {
    "path": "src/fecstream/fec-stream-stats.h",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef FECSTREAMSTATS_H\n#define FECSTREAMSTATS_H\n\n#include \"ortp/rtpsession.h\"\n#include <string>\n#include <unordered_map>\n#include <vector>\n\nnamespace ortp {\n\n#ifdef _WIN32\n// Disable C4251 triggered by need to export all stl template classes\n#pragma warning(disable : 4251)\n#endif // ifdef _WIN32\n\nclass FecStreamStats {\nprivate:\n\tstd::vector<uint16_t> mLostPackets;                   // sequence numbers of the lost packets\n\t                                                      // that are not found when the jitter buffer is read\n\tstd::vector<uint16_t> mRepairedPackets;               // sequence numbers of the packets repaired by FEC\n\tstd::unordered_map<uint16_t, size_t> mMissingPackets; // number of repair attempts of each packet\n\t                                                      // given its sequence number\n\tfec_stats mFecStats;\n\tconst size_t mMaxSize = 100;\n\tconst size_t mBins = 31;\n\n\t// histograms, bins from 0 to mBins - 1\n\tstd::vector<uint8_t> mLocalHistoRecovering; // number of attemps before a successful repair\n\tstd::vector<uint8_t> mLocalHistoLost;       // number of repair attemps of definitely lost packets\n\tstd::vector<uint8_t> mLocalHistoMissingGap; // number of packets between two lost packets\n\tstd::vector<uint8_t> mLocalHistoGapSize;    // number of consecutive lost packets\n\tstd::vector<uint8_t> mGlobalHistoRecovering;\n\tstd::vector<uint8_t> mGlobalHistoLost;\n\tstd::vector<uint8_t> mGlobalHistoMissingGap;\n\tstd::vector<uint8_t> mGlobalHistoGapSize;\n\n\tvoid printHistoAndClear();\n\tvoid printGlobalHistoAndClear();\n\tvoid printLostPacketsHisto();\n\tstd::string histoToString(const std::vector<uint8_t> &histo) const;\n\npublic:\n\tFecStreamStats();\n\t~FecStreamStats(){};\n\tvoid rowRepairSent() {\n\t\tmFecStats.row_repair_sent++;\n\t};\n\tvoid colRepairSent() {\n\t\tmFecStats.col_repair_sent++;\n\t};\n\tvoid rowRepairReceived(uint64_t cpt) {\n\t\tmFecStats.row_repair_received = cpt;\n\t};\n\tvoid colRepairReceived(uint64_t cpt) {\n\t\tmFecStats.col_repair_received = cpt;\n\t};\n\tuint64_t getPacketsLost() const {\n\t\treturn mFecStats.packets_lost;\n\t};\n\tuint64_t getPacketsRecovered() const {\n\t\treturn mFecStats.packets_recovered;\n\t};\n\tuint64_t getPacketsNotRecovered() const {\n\t\treturn mFecStats.packets_not_recovered;\n\t};\n\tfec_stats *getFecStats() {\n\t\treturn &mFecStats;\n\t};\n\tvoid askedPacket(uint16_t seqNum);\n\tvoid repairedPacket(uint16_t seqNum);\n\tvoid definitelyLostPacket(uint16_t newSeqNumReceived, int16_t diff);\n\tvoid printStats(RtpSession *sourceSession, RtpSession *fecSession);\n\tvoid clearAll();\n};\n\n} // namespace ortp\n#endif"
  },
  {
    "path": "src/fecstream/fecstream.cc",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <inttypes.h>\n\n#include \"fecstream.h\"\n\n#define HEADER_1 '\\x80'\n#define HEADER_2 '\\0'\n\nusing namespace ortp;\n\nextern \"C\" FecStream *fec_stream_new(struct _RtpSession *source, struct _RtpSession *fec, FecParams *fecParams) {\n\treturn (FecStream *)new FecStreamCxx(source, fec, (FecParamsController *)fecParams);\n}\nextern \"C\" void fec_stream_destroy(FecStream *fec_stream) {\n\tdelete (FecStreamCxx *)fec_stream;\n}\nextern \"C\" void fec_stream_unsubscribe(FecStream *fec_stream, FecParams *fecParams) {\n\t((FecStreamCxx *)fec_stream)->removeFromParamSubscribers((FecParamsController *)fecParams);\n}\nextern \"C\" void fec_stream_reset_cluster(FecStream *fec_stream) {\n\t((FecStreamCxx *)fec_stream)->resetCluster();\n}\nextern \"C\" void fec_stream_receive_repair_packet(FecStream *fec_stream, uint32_t timestamp) {\n\t((FecStreamCxx *)fec_stream)->receiveRepairPacket(timestamp);\n}\nextern \"C\" mblk_t *fec_stream_find_missing_packet(FecStream *fec_stream, uint16_t seqnum) {\n\treturn ((FecStreamCxx *)fec_stream)->findMissingPacket(seqnum);\n}\nextern \"C\" RtpSession *fec_stream_get_fec_session(FecStream *fec_stream) {\n\treturn ((FecStreamCxx *)fec_stream)->getFecSession();\n}\nextern \"C\" void fec_stream_count_lost_packets(FecStream *fec_stream, uint16_t new_seqnum_received, int16_t diff) {\n\t((FecStreamCxx *)fec_stream)->countLostPackets(new_seqnum_received, diff);\n}\nextern \"C\" void fec_stream_print_stats(FecStream *fec_stream) {\n\t((FecStreamCxx *)fec_stream)->printStats();\n}\nextern \"C\" fec_stats *fec_stream_get_stats(FecStream *fec_stream) {\n\treturn ((FecStreamCxx *)fec_stream)->getStats();\n}\nextern \"C\" bool_t fec_stream_enabled(FecStream *fec_stream) {\n\treturn ((FecStreamCxx *)fec_stream)->isEnabled();\n}\nextern \"C\" float fec_stream_get_overhead(FecStream *fec_stream) {\n\treturn ((FecStreamCxx *)fec_stream)->getMeasuredOverhead();\n}\nextern \"C\" void fec_stream_reset_overhead_measure(FecStream *fec_stream) {\n\t((FecStreamCxx *)fec_stream)->resetMeasuredOverhead();\n}\n\nFecStreamCxx::FecStreamCxx(struct _RtpSession *source, struct _RtpSession *fec, FecParamsController *fecParams)\n    : mEncoder(fecParams), mCluster(source, fecParams->getRepairWindow()), mIsEnabled(false) {\n\tmSourceSession = source;\n\tmFecSession = fec;\n\trtp_session_enable_jitter_buffer(mFecSession, FALSE);\n\tmSourceSession->fec_stream = (FecStream *)this;\n\tmFecSession->fec_stream = NULL;\n\tqinit(&mSourcePackets);\n\tRtpBundle *bundle = mSourceSession->bundle;\n\tRtpSession *session = rtp_bundle_get_primary_session(bundle);\n\trtp_session_get_transports(session, &mTransport, NULL);\n\tmModifier = ortp_new0(RtpTransportModifier, 1);\n\tmModifier->level = RtpTransportModifierLevelForwardErrorCorrection;\n\tmModifier->data = this;\n\tmModifier->t_process_on_send = FecStreamCxx::processOnSend;\n\tmModifier->t_process_on_receive = FecStreamCxx::processOnReceive;\n\tmModifier->t_process_on_schedule = NULL;\n\tmModifier->t_destroy = modifierFree;\n\tmeta_rtp_transport_append_modifier(mTransport, mModifier);\n\n\tmEncoderUpdate.L = fecParams->getL();\n\tmEncoderUpdate.D = fecParams->getD();\n\tmEncoderUpdate.is2D = fecParams->is2D();\n\tmEncoderUpdate.isUpdated = false;\n\tfecParams->addSubscriber(this);\n\tmEncoder.init(mFecSession, mSourceSession);\n\tmMeasuredOverhead.reset(0);\n\tmEncoderUpdate.isUpdated = true;\n}\n\nFecStreamCxx::~FecStreamCxx() {\n\tstd::lock_guard<std::mutex> guard(mQueueMutex);\n\tflushq(&mSourcePackets, FLUSHALL);\n\n\tmeta_rtp_transport_remove_modifier(mTransport, mModifier);\n\tmodifierFree(mModifier);\n\tmModifier = nullptr;\n\tmTransport = nullptr;\n}\n\nvoid FecStreamCxx::removeFromParamSubscribers(FecParamsController *fecParams) {\n\tfecParams->removeSubscriber(this);\n}\n\nint FecStreamCxx::processOnSend(struct _RtpTransportModifier *m, mblk_t *packet) {\n\n\tFecStreamCxx *fecStream = (FecStreamCxx *)m->data;\n\tRtpSession *sourceSession = fecStream->getSourceSession();\n\tsize_t ret = msgdsize(packet);\n\tuint32_t ssrc = rtp_get_ssrc(packet);\n\n\tif (!fecStream->isEnabled()) return (int)ret;\n\n\tif (ssrc == rtp_session_get_send_ssrc(sourceSession)) {\n\t\tfecStream->onNewSourcePacketSent(copymsg(packet));\n\t}\n\treturn (int)ret;\n}\n\nint FecStreamCxx::processOnReceive(struct _RtpTransportModifier *m, mblk_t *packet) {\n\tFecStreamCxx *fecStream = (FecStreamCxx *)m->data;\n\tRtpSession *sourceSession = fecStream->getSourceSession();\n\tuint32_t ssrc = rtp_get_ssrc(packet);\n\tsize_t ret = msgdsize(packet);\n\tif (ssrc == rtp_session_get_recv_ssrc(sourceSession)) {\n\t\tfecStream->onNewSourcePacketReceived(packet);\n\t}\n\treturn (int)ret;\n}\n\nvoid ortp::modifierFree(struct _RtpTransportModifier *m) {\n\tortp_free(m);\n}\n\nvoid FecStreamCxx::onNewSourcePacketSent(mblk_t *packet) {\n\n\tuint16_t seqnum = rtp_get_seqnumber(packet);\n\tuint32_t timestamp = rtp_get_timestamp(packet);\n\n\tmsgpullup(packet, -1);\n\tif (rtp_get_version(packet) != 2) return;\n\n\tstd::lock_guard<std::recursive_mutex> guard(mSendMutex);\n\tif (!mEncoderUpdate.isUpdated) updateEncoder();\n\tif (!mIsEnabled) return;\n\tif (mEncoder.isFull() || mEncoder.isEmpty()) {\n\t\tmEncoder.reset(seqnum);\n\t\tmMeasuredOverhead.resetEncoder();\n\t}\n\n\tauto source = std::make_shared<FecSourcePacket>(packet);\n\tmEncoder.add(*source);\n\tsize_t msgSizeSource = msgdsize(packet);\n\n\tmMeasuredOverhead.sendSourcePacket(msgSizeSource, mEncoder.getCurrentColumn());\n\n\tif (mEncoder.isRowFull()) {\n\t\tint i = mEncoder.getCurrentRow();\n\t\tmblk_t *rowRepair = mEncoder.getRowRepairMblk(i);\n\t\tif (rowRepair) {\n\t\t\trtp_set_timestamp(rowRepair, timestamp);\n\t\t\trtp_set_seqnumber(rowRepair, rtp_session_get_seq_number(mFecSession));\n\t\t\tortp_debug(\"row repair sent [%u] | %u | seqNumBase %u\", timestamp, rtp_get_seqnumber(rowRepair),\n\t\t\t           mEncoder.getRowRepair(i)->getSeqnumBase());\n\t\t\tsize_t msgSizeRepair = msgdsize(rowRepair);\n\t\t\tmMeasuredOverhead.sendRepairPacket(msgSizeRepair, mEncoder.getCurrentColumn());\n\t\t\trtp_session_sendm_with_ts(mFecSession, rowRepair, timestamp);\n\t\t\tmStats.rowRepairSent();\n\t\t}\n\t}\n\tif (mEncoder.isColFull()) {\n\t\tint i = mEncoder.getCurrentColumn();\n\t\tmblk_t *colRepair = mEncoder.getColRepairMblk(i);\n\t\tif (colRepair) {\n\t\t\trtp_set_timestamp(colRepair, timestamp);\n\t\t\trtp_set_seqnumber(colRepair, rtp_session_get_seq_number(mFecSession));\n\t\t\tortp_debug(\"col repair sent  [%u] | %u | seqNumBase %u\", timestamp, rtp_get_seqnumber(colRepair),\n\t\t\t           mEncoder.getColRepair(i)->getSeqnumBase());\n\t\t\tsize_t msgSizeRepair = msgdsize(colRepair);\n\t\t\tmMeasuredOverhead.sendRepairPacket(msgSizeRepair, mEncoder.getCurrentColumn());\n\t\t\trtp_session_sendm_with_ts(mFecSession, colRepair, timestamp);\n\t\t\tmStats.colRepairSent();\n\t\t}\n\t}\n\n\tif (mEncoder.isFull()) {\n\t\tmMeasuredOverhead.encoderFull();\n\t}\n}\n\nvoid FecStreamCxx::onNewSourcePacketReceived(mblk_t *packet) {\n\tmsgpullup(packet, -1);\n\tif (rtp_get_version(packet) != 2) return;\n\tmblk_t *packet_copy = copymsg(packet);\n\n\t// To avoid thread contention between the audio thread that receives the source packets and the video stream that\n\t// uses them for lost packet recovery with flexible FEC, the source packets received are copied into a queue by the\n\t// audio thread and transferred to the receive cluster by the video thread for the FEC processings.\n\tstd::lock_guard<std::mutex> guard(mQueueMutex);\n\tputq(&mSourcePackets, packet_copy);\n\n\t// The size of the queue is controlled because if no repair packet is received (for example when the FEC is\n\t// disabled) and any source packet is lost, the source packets are never transferred to the receive cluster\n\tif (mSourcePackets.q_mcount > 100) {\n\t\tmblk_t *erase = qbegin(&mSourcePackets);\n\t\tremq(&mSourcePackets, erase);\n\t\tif (erase != NULL) freemsg(erase);\n\t}\n}\n\nvoid FecStreamCxx::receiveRepairPacket(uint32_t timestamp) {\n\n\tmblk_t *repair_packet = rtp_session_recvm_with_ts(mFecSession, timestamp);\n\tif (repair_packet == NULL) return;\n\n\t// add last source packets\n\tupdateReceivedSourcePackets();\n\n\t// add new repair packet\n\tauto repair = std::make_shared<FecRepairPacket>(repair_packet);\n\tstd::lock_guard<std::recursive_mutex> guard(mReceiveMutex);\n\tmCluster.add(repair);\n\tmStats.rowRepairReceived(mCluster.getRowRepairCpt());\n\tmStats.colRepairReceived(mCluster.getColRepairCpt());\n\tfreemsg(repair_packet);\n}\n\nvoid FecStreamCxx::resetCluster() {\n\tstd::lock_guard<std::recursive_mutex> guard(mReceiveMutex);\n\tmCluster.reset();\n\tmStats.clearAll();\n}\n\nvoid FecStreamCxx::updateReceivedSourcePackets() {\n\tstd::lock_guard<std::mutex> guard(mQueueMutex);\n\twhile (mSourcePackets.q_mcount > 0) {\n\t\tmblk_t *mp = getq(&mSourcePackets);\n\t\tmCluster.add(mp);\n\t}\n}\n\nvoid FecStreamCxx::countLostPackets(uint16_t newSeqnumReceived, int16_t diff) {\n\tmStats.definitelyLostPacket(newSeqnumReceived, diff);\n}\n\nvoid FecStreamCxx::printStats() {\n\tmStats.printStats(mSourceSession, mFecSession);\n}\n\nmblk_t *FecStreamCxx::findMissingPacket(uint16_t seqnum) {\n\n\tmStats.askedPacket(seqnum);\n\tstd::shared_ptr<FecSourcePacket> packet;\n\n\t// recover\n\tupdateReceivedSourcePackets();\n\t{\n\t\tstd::lock_guard<std::recursive_mutex> guard(mReceiveMutex);\n\t\tmCluster.repair(seqnum);\n\t\tpacket = mCluster.getSourcePacket(seqnum);\n\t}\n\tif (!packet) {\n\t\treturn nullptr;\n\t}\n\n\t// apply modifier\n\tmblk_t *mp = packet->getPacketCopy();\n\tRtpTransport *transport = NULL;\n\tortp_mutex_lock(&mSourceSession->main_mutex);\n\tRtpBundle *bundle = mSourceSession->bundle;\n\tRtpSession *session = bundle ? rtp_bundle_get_primary_session(bundle) : nullptr;\n\tortp_mutex_unlock(&mSourceSession->main_mutex);\n\tif (session) {\n\t\trtp_session_get_transports(session, &transport, NULL);\n\t\tif (meta_rtp_transport_apply_all_except_one_on_receive(transport, mModifier, mp) >= 0) {\n\t\t\tmStats.repairedPacket(seqnum);\n\t\t\tortp_debug(\"fecstream[%p] Source packet recovered : SeqNum = %u, current stats : %u lost, %u recovered, %u \"\n\t\t\t           \"not repaired\",\n\t\t\t           this, rtp_get_seqnumber(mp), static_cast<unsigned int>(mStats.getPacketsLost()),\n\t\t\t           static_cast<unsigned int>(mStats.getPacketsRecovered()),\n\t\t\t           static_cast<unsigned int>(mStats.getPacketsNotRecovered()));\n\t\t\treturn mp;\n\t\t}\n\t}\n\tfreemsg(mp);\n\treturn nullptr;\n}\n\nRtpSession *FecStreamCxx::getFecSession() const {\n\treturn mFecSession;\n}\n\nRtpSession *FecStreamCxx::getSourceSession() const {\n\treturn mSourceSession;\n}\n\nfec_stats *FecStreamCxx::getStats() {\n\treturn mStats.getFecStats();\n}\n\nbool FecStreamCxx::isEnabled() {\n\tstd::lock_guard<std::recursive_mutex> guard(mSendMutex);\n\treturn mIsEnabled;\n}\n\nvoid FecStreamCxx::updateEncoder() {\n\tif (mIsEnabled) {\n\t\tmEncoder.update(mEncoderUpdate.L, mEncoderUpdate.D, mEncoderUpdate.is2D);\n\t} else {\n\t\tmEncoder.clear();\n\t}\n\tmEncoderUpdate.isUpdated = true;\n}\n\nvoid FecStreamCxx::update(FecParamsController *params) {\n\tstd::lock_guard<std::recursive_mutex> guard(mSendMutex);\n\tmEncoderUpdate.isUpdated = false;\n\tsize_t overheadColNb = 1;\n\tif (params->getEnabled()) {\n\t\tmIsEnabled = true;\n\t\tmEncoderUpdate.L = params->getL();\n\t\tmEncoderUpdate.D = params->getD();\n\t\tmEncoderUpdate.is2D = params->is2D();\n\t\tif (!mEncoderUpdate.is2D && mEncoderUpdate.D > 0) {\n\t\t\toverheadColNb = static_cast<size_t>(mEncoderUpdate.L);\n\t\t}\n\t} else {\n\t\tmIsEnabled = false;\n\t};\n\tresetMeasuredOverhead(overheadColNb);\n}\n\nvoid FecStreamCxx::resetMeasuredOverhead() {\n\tstd::lock_guard<std::recursive_mutex> guard(mSendMutex);\n\tmMeasuredOverhead.reset(1);\n}\n\nvoid FecStreamCxx::resetMeasuredOverhead(size_t columnNumber) {\n\tstd::lock_guard<std::recursive_mutex> guard(mSendMutex);\n\tmMeasuredOverhead.reset(columnNumber);\n}\n\nfloat FecStreamCxx::getMeasuredOverhead() {\n\tstd::lock_guard<std::recursive_mutex> guard(mSendMutex);\n\treturn mMeasuredOverhead.computeOverheadEstimator();\n};\n"
  },
  {
    "path": "src/fecstream/fecstream.h",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef FECSTREAM_H\n#define FECSTREAM_H\n\n#include \"fec-encoder.h\"\n#include \"fec-stream-stats.h\"\n#include \"overhead.h\"\n#include \"receive-cluster.h\"\n#include <mutex>\n#include <ortp/rtpsession.h>\nnamespace ortp {\n\n#ifdef _WIN32\n// Disable C4251 triggered by need to export all stl template classes\n#pragma warning(disable : 4251)\n#endif // ifdef _WIN32\n\nclass ORTP_PUBLIC FecStreamCxx : public FecParamsSubscriber {\n\nprivate:\n\tRtpSession *mSourceSession = nullptr;\n\tRtpSession *mFecSession = nullptr;\n\tFecEncoder mEncoder;\n\tReceiveCluster mCluster;\n\tFecStreamStats mStats;\n\tRtpTransport *mTransport = nullptr;\n\tRtpTransportModifier *mModifier = nullptr;\n\tbool mIsEnabled;\n\tOverhead mMeasuredOverhead;\n\tqueue_t mSourcePackets;\n\tstd::mutex mQueueMutex;\n\tstd::recursive_mutex mSendMutex;\n\tstd::recursive_mutex mReceiveMutex;\n\n\tstruct {\n\t\tuint8_t L;\n\t\tuint8_t D;\n\t\tbool is2D;\n\t\tbool isUpdated;\n\t} mEncoderUpdate;\n\n\tvoid updateReceivedSourcePackets();\n\tvoid updateEncoder();\n\npublic:\n\tFecStreamCxx(struct _RtpSession *source, struct _RtpSession *fec, FecParamsController *fecParams);\n\tvoid removeFromParamSubscribers(FecParamsController *fecParams);\n\tstatic int processOnSend(struct _RtpTransportModifier *m, mblk_t *packet);\n\tstatic int processOnReceive(struct _RtpTransportModifier *m, mblk_t *packet);\n\tvoid onNewSourcePacketSent(mblk_t *packet);\n\tvoid onNewSourcePacketReceived(mblk_t *packet);\n\tvoid receiveRepairPacket(uint32_t timestamp);\n\tvoid resetCluster();\n\tmblk_t *findMissingPacket(uint16_t seqnum);\n\tRtpSession *getFecSession() const;\n\tRtpSession *getSourceSession() const;\n\tfec_stats *getStats();\n\tvoid countLostPackets(uint16_t newSeqnumReceived, int16_t diff);\n\tvoid printStats();\n\tvoid update(FecParamsController *) override;\n\tbool isEnabled();\n\tfloat getMeasuredOverhead();\n\tvoid resetMeasuredOverhead();\n\tvoid resetMeasuredOverhead(size_t columnNumber);\n\t~FecStreamCxx();\n};\n\nvoid modifierFree(struct _RtpTransportModifier *m);\n} // namespace ortp\n#endif\n"
  },
  {
    "path": "src/fecstream/overhead.cpp",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <algorithm>\n\n#include \"overhead.h\"\n\n#define HEADER_1 '\\x80'\n#define HEADER_2 '\\0'\n\nusing namespace ortp;\n\nOverhead::Overhead() {\n\tmL = 1;\n\tmRepairSizesInEncoder.assign(mL, 0);\n\tmSourceSizesInEncoder.assign(mL, 0);\n}\n\nvoid Overhead::sendSourcePacket(size_t msgSize, int i) {\n\tif (mL == 1) {\n\t\tmSourceSizesInEncoder.at(0) += msgSize;\n\t} else {\n\t\tmSourceSizesInEncoder.at(i) += msgSize;\n\t}\n}\n\nvoid Overhead::sendRepairPacket(size_t msgSize, int i) {\n\tif (mL == 1) {\n\t\tmRepairSizesInEncoder.at(0) += msgSize;\n\t} else {\n\t\tmRepairSizesInEncoder.at(i) += msgSize;\n\t}\n}\n\nvoid Overhead::encoderFull() {\n\tfor (size_t i = 0; i < mRepairSizesInEncoder.size(); i++) {\n\t\tfloat current_overhead =\n\t\t    (mSourceSizesInEncoder.at(i) == 0)\n\t\t        ? 0.f\n\t\t        : static_cast<float>(mRepairSizesInEncoder.at(i)) / static_cast<float>(mSourceSizesInEncoder.at(i));\n\t\tmOverheads.push(current_overhead);\n\t}\n\twhile (mOverheads.size() > mBlocksNumber) {\n\t\tmOverheads.pop();\n\t}\n\tresetEncoder(mRepairSizesInEncoder.size());\n}\n\nvoid Overhead::resetEncoder() {\n\tmRepairSizesInEncoder.assign(mL, 0);\n\tmSourceSizesInEncoder.assign(mL, 0);\n}\n\nvoid Overhead::resetEncoder(size_t L) {\n\tmL = L;\n\tmRepairSizesInEncoder.assign(L, 0);\n\tmSourceSizesInEncoder.assign(L, 0);\n}\n\nvoid Overhead::reset(size_t L) {\n\tmL = L;\n\twhile (!mOverheads.empty()) {\n\t\tmOverheads.pop();\n\t}\n\tresetEncoder(L);\n}\n\nfloat Overhead::computeOverheadEstimator() {\n\tfloat overhead_size = static_cast<float>(mOverheads.size());\n\tif (overhead_size < 5.f) return 0.f;\n\n\tstd::queue<float> copy_overhead = mOverheads;\n\tfloat mean_overhead = 0.f;\n\twhile (!copy_overhead.empty()) {\n\t\tmean_overhead += copy_overhead.front();\n\t\tcopy_overhead.pop();\n\t}\n\tmean_overhead = mean_overhead / overhead_size;\n\treturn mean_overhead;\n}"
  },
  {
    "path": "src/fecstream/overhead.h",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <queue>\n\n#ifndef OVERHEAD_H\n#define OVERHEAD_H\n\nnamespace ortp {\n\n#ifdef _WIN32\n// Disable C4251 triggered by need to export all stl template classes\n#pragma warning(disable : 4251)\n#endif // ifdef _WIN32\n\n/** @class Overhead\n * @brief Class to estimate the overhead between repair and source packets sent.\n *\n *  This class compute the mean overhead over the last FEC blocks sent from the ratio between the sizes of the repair\n * packets and source packets sent for each block. A local overhead is computed each time the encoderFull method is\n * called, for each independent column of a FEC block.\n *\n * The overhead of a FEC block is defined as the ratio (total size of the repair packets)/(total size of the source\n * packets).\n *\n * The overhead estimator is the average value of the last mBlocksNumber overhead measurements. The estimator can be\n * reset to 0 to collect new values, for example if the parameters ofthe FEC encoder have changed, or the settings of\n * the video encoder that can modidify the size of the packets. A FEC block is a set of successive source packets and\n * the repair packets that protect them. One overhead is computed per FEC block. In case of 1D interleaved parity\n * protection, the FEC blocks are interleaved and the source packets are sent for L independent columns. In that case, L\n * overheads can be measured in parallel, by setting mL = L.\n */\nclass Overhead {\n\nprivate:\n\tsize_t const mBlocksNumber = 50; /**< Number of last FEC blocks used to compute the overhead estimator. */\n\tstd::queue<float> mOverheads;    /**< Last measures of overheads. Its maximal size is mBlocksNumber. */\n\tsize_t mL;                       /**< Number of independant overheads to measure in parallel. By default is 1. */\n\tstd::vector<size_t> mSourceSizesInEncoder; /**< Sum of sizes of the source packets of the current FEC blocks. There\n\t                                              is one element per independent measure. By default has 1 element. */\n\tstd::vector<size_t> mRepairSizesInEncoder; /**< Sum of sizes of the repair packets of the current FEC block. There\n\t                                              is one element per independent measure. By default has 1 element. */\n\npublic:\n\tOverhead();\n\n\t/**\n\t * Add the size of a source packet to the i-est overhead measure. If mL = 1, the size is added to the unique element\n\t * in mSourceSizesInEncoder.\n\t *\n\t * @param msgSize size of the source packet.\n\t * @param i Index of the current measure in mSourceSizesInEncoder if mL > 1, ignored otherwise.\n\t */\n\tvoid sendSourcePacket(size_t msgSize, int i);\n\n\t/**\n\t * Add the size of a repair packet to the i-est overhead measure. If mL = 1, the size is added to the unique element\n\t * in mRepairSizesInEncoder.\n\t *\n\t * @param msgSize size of the repair packet.\n\t * @param i Index of the current measure in mRepairSizesInEncoder if mL > 1, ignored otherwise.\n\t */\n\tvoid sendRepairPacket(size_t msgSize, int i);\n\n\t/**\n\t * Compute the current overhead from the current measurements of the packets sizes. If severeal overhead are\n\t * measured in parallel, they are all computed. The resulting values are added in mOverheads. If the size of\n\t * mOverheads exceed the maximal valu, the oldest measures are erased.\n\t */\n\tvoid encoderFull();\n\n\t/**\n\t * Reset the current source and packet sizes to 0.\n\t */\n\tvoid resetEncoder();\n\n\t/**\n\t * Reset the current source and packet sizes to 0 and set the number of overhead to measure in parallel to L.\n\t *\n\t * @param L Number of independant overheads to measure in parallel. L must be > 1 for 1D interleaved parity\n\t * protection only.\n\t */\n\tvoid resetEncoder(size_t L);\n\n\t/**\n\t * Reset all measurements and estimator and set the number of overhead to measure in parallel to L.\n\t *\n\t * @param L Number of independant overheads to measure in parallel. L must be > 1 for 1D interleaved parity\n\t * protection only.\n\t */\n\tvoid reset(size_t L);\n\n\t/**\n\t * Compute and return the estimation of the overhead as the average value of the last mBlocksNumber overheads\n\t * measured in mOverheads.\n\t */\n\tfloat computeOverheadEstimator();\n\n\t~Overhead(){};\n};\n\n} // namespace ortp\n#endif\n"
  },
  {
    "path": "src/fecstream/packet-api.cpp",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"packet-api.h\"\nusing namespace ortp;\n\nBitstring::Bitstring() {\n\tmemset(&mBuffer[0], 0, 8);\n}\n\nBitstring::Bitstring(const mblk_t *packet) {\n\n\tsize_t payload_size = msgdsize(packet) - RTP_FIXED_HEADER_SIZE;\n\tmBuffer[0] =\n\t    rtp_get_version(packet) << 6 | rtp_get_padbit(packet) << 5 | rtp_get_extbit(packet) << 4 | rtp_get_cc(packet);\n\tmBuffer[1] = rtp_get_markbit(packet) << 7 | rtp_get_payload_type(packet);\n\tsetTimestamp((uint32_t)rtp_get_timestamp(packet));\n\tsetLength((uint16_t)payload_size);\n}\n\nvoid Bitstring::add(Bitstring const &other) {\n\n\t*(uint64_t *)&mBuffer[0] ^= *(uint64_t *)&other.mBuffer[0];\n}\n\nvoid Bitstring::reset() {\n\tmemset(&mBuffer[0], 0, 8);\n}\n\nvoid Bitstring::setHeader(uint16_t *h) {\n\tmBuffer[0] = (*h & 0xff);\n\tmBuffer[1] = (*h >> 8);\n}\n\nvoid Bitstring::setLength(uint16_t l) {\n\tmBuffer[6] = l & 0xff;\n\tmBuffer[7] = (l >> 8);\n}\n\nvoid Bitstring::setTimestamp(uint32_t t) {\n\tmBuffer[2] = t & 0xff;\n\tmBuffer[3] = (t >> 8) & 0xff;\n\tmBuffer[4] = (t >> 16) & 0xff;\n\tmBuffer[5] = ((t >> 16) >> 8);\n}\n\nbool Bitstring::equals(Bitstring const &other) {\n\treturn (memcmp(&mBuffer[0], &other.mBuffer[0], 8) == 0);\n}\n\nvoid Bitstring::write(mblk_t *packet) {\n\trtp_set_version(packet, 2);\n\trtp_set_padbit(packet, (mBuffer[0] >> 5) & 0x1);\n\trtp_set_extbit(packet, (mBuffer[0] >> 4) & 0x1);\n\trtp_set_cc(packet, (mBuffer[0]) & 0xF);\n\trtp_set_markbit(packet, (mBuffer[1] >> 7) & 0x1);\n\trtp_set_payload_type(packet, mBuffer[1] & 0x7F);\n\trtp_set_timestamp(packet, getTimestamp());\n}\n\nFecSourcePacket::FecSourcePacket(const struct _RtpSession *session, const Bitstring &bs) {\n\tmPacket = allocb(RTP_FIXED_HEADER_SIZE, BPRI_MED);\n\tmPacket->b_wptr += RTP_FIXED_HEADER_SIZE;\n\trtp_set_ssrc(mPacket, rtp_session_get_send_ssrc(session));\n\tmBitstring.add(bs);\n}\n\nFecSourcePacket::FecSourcePacket(mblk_t *incoming) : mBitstring(incoming) {\n\tmPacket = incoming;\n}\n\nvoid FecSourcePacket::initPayload(uint16_t length) {\n\n\tmsgpullup(mPacket, msgdsize(mPacket) + length);\n\tmemset(mPacket->b_wptr, 0, length);\n\tmPacket->b_wptr += length;\n}\n\nvoid FecSourcePacket::addBitstring(Bitstring const &other) {\n\tmBitstring.add(other);\n}\n\nvoid FecSourcePacket::addPayload(const uint8_t *toAdd, size_t size) {\n\n\tuint8_t *wptr = NULL;\n\tuint8_t *rptr = (uint8_t *)toAdd;\n\tsize_t currentSize = getPayloadBuffer(&wptr);\n\tsize_t minSize = (size < currentSize) ? size : currentSize;\n\tfor (size_t i = 0; i < minSize; i++) {\n\t\t*wptr ^= *rptr;\n\t\twptr++;\n\t\trptr++;\n\t}\n}\n\nvoid FecSourcePacket::addPayload(FecSourcePacket const &other) {\n\n\tuint8_t *other_rptr = NULL;\n\tsize_t otherPayloadSize = other.getPayloadBuffer(&other_rptr);\n\taddPayload(other_rptr, otherPayloadSize);\n}\n\nvoid FecSourcePacket::add(FecSourcePacket const &other) {\n\n\taddBitstring(other.mBitstring);\n\taddPayload(other);\n}\n\nvoid FecSourcePacket::writeBitstring() {\n\tmBitstring.write(mPacket);\n}\n\nsize_t FecSourcePacket::getPayloadBuffer(uint8_t **start) const {\n\t*start = mPacket->b_rptr + RTP_FIXED_HEADER_SIZE;\n\treturn msgdsize(mPacket) - RTP_FIXED_HEADER_SIZE;\n}\n\nmblk_t *FecSourcePacket::transfer() {\n\tif (mPacket) {\n\t\tmblk_t *ret = mPacket;\n\t\tmPacket = nullptr;\n\t\treturn ret;\n\t}\n\treturn nullptr;\n}\n\nmblk_t *FecSourcePacket::getPacket() const {\n\treturn mPacket;\n}\n\nmblk_t *FecSourcePacket::getPacketCopy() const {\n\treturn copymsg(mPacket);\n}\n\nconst Bitstring &FecSourcePacket::getBitstring() const {\n\treturn mBitstring;\n}\n\nvoid FecSourcePacket::setSsrc(uint32_t ssrc) {\n\trtp_set_ssrc(mPacket, ssrc);\n}\n\nvoid FecSourcePacket::setSequenceNumber(uint16_t seqnum) {\n\trtp_set_seqnumber(mPacket, seqnum);\n}\n\nuint16_t FecSourcePacket::getSequenceNumber() const {\n\treturn rtp_get_seqnumber(mPacket);\n}\n\nFecSourcePacket::~FecSourcePacket() {\n\tif (mPacket) {\n\t\tfreemsg(mPacket);\n\t}\n}\n\nFecRepairPacket::FecRepairPacket(\n    struct _RtpSession *fecSession, struct _RtpSession *sourceSession, uint16_t seqnumBase, uint8_t L, uint8_t D) {\n\tmPacket = NULL;\n\tthis->mSeqnumBase = seqnumBase;\n\tthis->mL = L;\n\tthis->mD = D;\n\tmPacket = rtp_session_create_repair_packet_header(\n\t    fecSession, sourceSession,\n\t    8 * sizeof(uint8_t) + sizeof(uint32_t)); // allocate extra size for initBitString, SeqNumBase, L and D\n\n\t// initBitstring\n\tmemset(mPacket->b_wptr, 0, 8 * sizeof(uint8_t));\n\tmPacket->b_wptr += 8 * sizeof(uint8_t);\n\t// writeSeqnumBase\n\t*(uint16_t *)mPacket->b_wptr = mSeqnumBase;\n\tmPacket->b_wptr += sizeof(uint16_t);\n\t// writeL\n\t*(uint8_t *)mPacket->b_wptr = mL;\n\tmPacket->b_wptr += sizeof(uint8_t);\n\t// writeD\n\t*(uint8_t *)mPacket->b_wptr = mD;\n\tmPacket->b_wptr += sizeof(uint8_t);\n}\n\nFecRepairPacket::FecRepairPacket(const mblk_t *repairPacket) {\n\n\tmPacket = copymsg(repairPacket);\n\tuint8_t *rptr = NULL;\n\tparametersStart(&rptr);\n\n\tmSeqnumBase = *(uint16_t *)rptr;\n\trptr += sizeof(uint16_t);\n\tmL = *(uint8_t *)rptr;\n\trptr += sizeof(uint8_t);\n\tmD = *(uint8_t *)rptr;\n}\n\nsize_t FecRepairPacket::bitstringStart(uint8_t **start) const {\n\trtp_get_payload(mPacket, start);\n\treturn (8 * sizeof(uint8_t));\n}\n\nsize_t FecRepairPacket::parametersStart(uint8_t **start) const {\n\n\tsize_t bitstringSize = bitstringStart(start);\n\t*start += bitstringSize;\n\treturn rtp_get_cc(mPacket) * sizeof(uint32_t);\n}\n\nsize_t FecRepairPacket::repairPayloadStart(uint8_t **start) const {\n\tsize_t parametersSize = parametersStart(start);\n\t*start += parametersSize;\n\treturn (mPacket->b_wptr - *start);\n}\n\nBitstring FecRepairPacket::extractBitstring() const {\n\n\tuint8_t *rptr = NULL;\n\tBitstring bs;\n\n\tbitstringStart(&rptr);\n\tbs.setHeader((uint16_t *)rptr);\n\trptr += sizeof(uint16_t);\n\tuint32_t timestamp =\n\t    ((uint32_t)rptr[3] << 24) | ((uint32_t)rptr[2] << 16) | ((uint32_t)rptr[1] << 8) | (uint32_t)rptr[0];\n\tbs.setTimestamp(timestamp);\n\trptr += sizeof(uint32_t);\n\tbs.setLength(ntohs(*(uint16_t *)rptr));\n\n\treturn bs;\n}\n\nvoid FecRepairPacket::addBitstring(Bitstring const &bitstring) {\n\tuint8_t *ptr = NULL;\n\tbitstringStart(&ptr);\n\t*(uint16_t *)ptr ^= bitstring.getHeader();\n\tptr += sizeof(uint16_t);\n\tbitstring.addTimestamp(ptr);\n\tptr += sizeof(uint32_t);\n\t*(uint16_t *)ptr ^= htons(bitstring.getLength());\n}\n\nvoid FecRepairPacket::addPayload(FecSourcePacket const &sourcePacket) {\n\n\tuint8_t *packet_rptr = NULL;\n\tuint8_t *repair_wptr = NULL;\n\n\tsize_t sourcePayloadSize = sourcePacket.getPayloadBuffer(&packet_rptr);\n\tsize_t repairPayloadSize = repairPayloadStart(&repair_wptr);\n\n\tif (sourcePayloadSize > repairPayloadSize) {\n\t\tsize_t diff = sourcePayloadSize - repairPayloadSize;\n\t\tmsgpullup(mPacket, msgdsize(mPacket) + diff);\n\t\tmemset(mPacket->b_wptr, 0, diff);\n\t\tmPacket->b_wptr += diff;\n\t}\n\trepairPayloadSize = repairPayloadStart(&repair_wptr);\n\tsize_t minSize = (repairPayloadSize > sourcePayloadSize) ? sourcePayloadSize : repairPayloadSize;\n\tfor (size_t i = 0; i < minSize; i++) {\n\t\t*repair_wptr ^= *packet_rptr;\n\t\trepair_wptr++;\n\t\tpacket_rptr++;\n\t}\n}\n\nvoid FecRepairPacket::add(FecSourcePacket const &sourcePacket) {\n\taddBitstring(sourcePacket.getBitstring());\n\taddPayload(sourcePacket);\n}\n\nstd::vector<uint16_t> FecRepairPacket::createSequenceNumberList() const {\n\tstd::vector<uint16_t> list;\n\tuint8_t step = ((mD <= 1) ? 1 : mL);\n\tuint16_t size = ((mD <= 1) ? mL : mD);\n\tlist.emplace_back(mSeqnumBase);\n\tfor (int i = 1; i < size; i++) {\n\t\tlist.emplace_back(list[i - 1] + step);\n\t}\n\treturn list;\n}\n\nvoid FecRepairPacket::reset(uint16_t seqnumBase) {\n\n\tthis->mSeqnumBase = seqnumBase;\n\tbitstringStart(&mPacket->b_wptr);\n\n\t// initBitstring\n\tmemset(mPacket->b_wptr, 0, 8 * sizeof(uint8_t));\n\tmPacket->b_wptr += 8 * sizeof(uint8_t);\n\t// writeSeqnumBase\n\t*(uint16_t *)mPacket->b_wptr = seqnumBase;\n\tmPacket->b_wptr += sizeof(uint16_t);\n\t// writeL\n\t*(uint8_t *)mPacket->b_wptr = mL;\n\tmPacket->b_wptr += sizeof(uint8_t);\n\t// writeD\n\t*(uint8_t *)mPacket->b_wptr = mD;\n\tmPacket->b_wptr += sizeof(uint8_t);\n}\n\nuint32_t FecRepairPacket::getProtectedSsrc() const {\n\treturn rtp_get_csrc(mPacket, 0);\n}\n\nuint8_t FecRepairPacket::getL() const {\n\treturn mL;\n}\n\nuint8_t FecRepairPacket::getD() const {\n\treturn mD;\n}\n\nuint16_t FecRepairPacket::getSeqnumBase() const {\n\treturn mSeqnumBase;\n};\n\nuint16_t FecRepairPacket::getSeqnum() const {\n\treturn rtp_get_seqnumber(mPacket);\n};\n\nmblk_t *FecRepairPacket::transfer() {\n\tif (mPacket) {\n\t\tmblk_t *ret = mPacket;\n\t\tmPacket = nullptr;\n\t\treturn ret;\n\t}\n\treturn nullptr;\n}\n\nmblk_t *FecRepairPacket::getRepairPacket() const {\n\treturn mPacket;\n};\n\nmblk_t *FecRepairPacket::getCopy() {\n\tif (mPacket) return copymsg(mPacket);\n\treturn nullptr;\n}\n\nFecRepairPacket::~FecRepairPacket() {\n\tif (mPacket) {\n\t\tfreemsg(mPacket);\n\t}\n}"
  },
  {
    "path": "src/fecstream/packet-api.h",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef PACKET_API_H\n#define PACKET_API_H\n\n#include <ortp/logging.h>\n#include <ortp/rtpsession.h>\n#include <vector>\n\nnamespace ortp {\nclass ORTP_PUBLIC Bitstring {\n\nprivate:\n\tuint8_t mBuffer[8];\n\npublic:\n\tBitstring();\n\tBitstring(const mblk_t *packet);\n\tvoid add(Bitstring const &other);\n\tvoid reset();\n\tvoid write(mblk_t *packet);\n\tinline uint16_t getHeader() const {\n\t\treturn *(uint16_t *)&mBuffer[0];\n\t}\n\tinline uint32_t getTimestamp() const {\n\t\tuint32_t timestamp = ((uint32_t)mBuffer[5] << 24) | ((uint32_t)mBuffer[4] << 16) | ((uint32_t)mBuffer[3] << 8) |\n\t\t                     (uint32_t)mBuffer[2];\n\t\treturn timestamp;\n\t}\n\tinline void addTimestamp(uint8_t *ptr) const {\n\t\tfor (uint8_t i = 0; i < 4; i++) {\n\t\t\tptr[i] ^= mBuffer[i + 2];\n\t\t}\n\t}\n\tinline uint16_t getLength() const {\n\t\treturn *(uint16_t *)&mBuffer[6];\n\t}\n\tvoid setHeader(uint16_t *h);\n\tvoid setLength(uint16_t l);\n\tvoid setTimestamp(uint32_t t);\n\tbool equals(Bitstring const &other);\n\t~Bitstring(){};\n};\n\nclass ORTP_PUBLIC FecSourcePacket {\n\nprivate:\n\tmblk_t *mPacket;\n\tBitstring mBitstring;\n\npublic:\n\tFecSourcePacket(const struct _RtpSession *session, const Bitstring &bs);\n\n\t/**\n\t * This constructor creates a FecSourcePacket from a packet. Warning: it does not copy the packet.\n\t *\n\t * @param incoming input source packet.\n\t */\n\tFecSourcePacket(mblk_t *incoming);\n\tFecSourcePacket(const FecSourcePacket &other) = delete;\n\tFecSourcePacket &operator=(const FecSourcePacket &other) = delete;\n\tvoid addBitstring(Bitstring const &other);\n\tvoid add(FecSourcePacket const &other);\n\tvoid writeBitstring();\n\tsize_t getPayloadBuffer(uint8_t **start) const;\n\tvoid addPayload(FecSourcePacket const &other);\n\tvoid addPayload(const uint8_t *toAdd, size_t size);\n\tvoid initPayload(uint16_t length);\n\tmblk_t *getPacket() const;\n\tmblk_t *getPacketCopy() const;\n\tconst Bitstring &getBitstring() const;\n\tmblk_t *transfer();\n\tvoid setSsrc(uint32_t ssrc);\n\tvoid setSequenceNumber(uint16_t seqnum);\n\tuint16_t getSequenceNumber() const;\n\t~FecSourcePacket();\n};\n\nclass ORTP_PUBLIC FecRepairPacket {\nprivate:\n\tmblk_t *mPacket;\n\tuint8_t mL;\n\tuint8_t mD;\n\tuint16_t mSeqnumBase;\n\npublic:\n\tFecRepairPacket();\n\tFecRepairPacket(const mblk_t *repairPacket);\n\tFecRepairPacket(const FecRepairPacket &other) = delete;\n\tFecRepairPacket &operator=(const FecRepairPacket &other) = delete;\n\n\tFecRepairPacket(\n\t    struct _RtpSession *fecSession, struct _RtpSession *sourceSession, uint16_t seqnumBase, uint8_t L, uint8_t D);\n\tvoid addBitstring(Bitstring const &bitstring);\n\tsize_t bitstringStart(uint8_t **start) const;\n\tBitstring extractBitstring() const;\n\tsize_t parametersStart(uint8_t **start) const;\n\n\tsize_t repairPayloadStart(uint8_t **start) const;\n\tuint32_t getProtectedSsrc() const;\n\tvoid addPayload(FecSourcePacket const &sourcePacket);\n\tvoid add(FecSourcePacket const &sourcePacket);\n\tvoid reset(uint16_t seqnumBase);\n\tstd::vector<uint16_t> createSequenceNumberList() const;\n\tuint8_t getL() const;\n\tuint8_t getD() const;\n\tuint16_t getSeqnumBase() const;\n\tuint16_t getSeqnum() const;\n\tmblk_t *transfer();\n\tmblk_t *getRepairPacket() const;\n\tmblk_t *getCopy();\n\t~FecRepairPacket();\n};\n} // namespace ortp\n#endif // PACKET_API_H"
  },
  {
    "path": "src/fecstream/receive-cluster.cpp",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"receive-cluster.h\"\n\nusing namespace ortp;\n\nReceiveCluster::ReceiveCluster(struct _RtpSession *session, uint32_t repairWindow) : mRepairWindow(repairWindow) {\n\tthis->mSession = session;\n\tmRowRepairCpt = 0;\n\tmColRepairCpt = 0;\n}\n\nvoid ReceiveCluster::cleanSource() {\n\tuint32_t lastTimestamp = mSourceTimeStamp.rbegin()->first;\n\tuint32_t firstTimestamp = mSourceTimeStamp.begin()->first;\n\tif (!mSource.empty() && lastTimestamp - firstTimestamp >= mRepairWindow) {\n\t\tauto itLimit = mSourceTimeStamp.upper_bound(lastTimestamp - mRepairWindow);\n\t\tfor (auto it = mSourceTimeStamp.begin(); it != itLimit; it++) {\n\t\t\tmSource.erase(it->second);\n\t\t}\n\t\tmSourceTimeStamp.erase(mSourceTimeStamp.begin(), itLimit);\n\t}\n}\n\nvoid ReceiveCluster::cleanRepair() {\n\tuint32_t lastTimestamp = mRepairTimeStamp.rbegin()->first;\n\tuint32_t firstTimestamp = mRepairTimeStamp.begin()->first;\n\tif (!mColRepairAll.empty() && lastTimestamp - firstTimestamp >= mRepairWindow) {\n\t\tauto itLimit = mRepairTimeStamp.upper_bound(lastTimestamp - mRepairWindow);\n\t\tfor (auto it = mRepairTimeStamp.begin(); it != itLimit; it++) {\n\t\t\tif (mRowRepairAll.count(it->second) != 0) {\n\t\t\t\tmRowRepairAll.erase(it->second);\n\t\t\t\tmFecGraph.cleanRowRepair(it->second);\n\t\t\t}\n\t\t\tif (mColRepairAll.count(it->second) != 0) {\n\t\t\t\tmColRepairAll.erase(it->second);\n\t\t\t\tmFecGraph.cleanColRepair(it->second);\n\t\t\t}\n\t\t}\n\t\tmRepairTimeStamp.erase(mRepairTimeStamp.begin(), itLimit);\n\t}\n}\n\nvoid ReceiveCluster::add(mblk_t *mp) {\n\tuint16_t seqnum = rtp_get_seqnumber(mp);\n\tauto packet = std::make_shared<FecSourcePacket>(mp);\n\tmSourceTimeStamp.emplace(packet->getBitstring().getTimestamp(), seqnum);\n\tcleanSource();\n\tif (mSourceTimeStamp.count(packet->getBitstring().getTimestamp()) > 0) {\n\t\tmSource.emplace(seqnum, packet);\n\t}\n}\n\nvoid ReceiveCluster::add(const std::shared_ptr<FecRepairPacket> &packet) {\n\tconst uint16_t seqnum = packet->getSeqnum();\n\tconst auto seqnumList = packet->createSequenceNumberList();\n\tconst uint32_t timestamp = rtp_get_timestamp(packet->getRepairPacket());\n\tmRepairTimeStamp.emplace(timestamp, seqnum);\n\tcleanRepair();\n\tif (packet->getD() > 1) {\n\t\tmColRepairCpt++;\n\t\tif (mRepairTimeStamp.count(timestamp) > 0) {\n\t\t\tmFecGraph.addColRepair(seqnum, seqnumList);\n\t\t\tmColRepairAll.emplace(seqnum, packet);\n\t\t}\n\t} else {\n\t\tmRowRepairCpt++;\n\t\tif (mRepairTimeStamp.count(timestamp) > 0) {\n\t\t\tmFecGraph.addRowRepair(seqnum, seqnumList);\n\t\t\tmRowRepairAll.emplace(seqnum, packet);\n\t\t}\n\t}\n}\n\nstd::shared_ptr<FecSourcePacket> ReceiveCluster::getSourcePacket(uint16_t seqnum) const {\n\tauto it = mSource.find(seqnum);\n\tif (it != mSource.end()) return it->second;\n\telse return nullptr;\n}\n\nvoid ReceiveCluster::repair(uint16_t seqNum) {\n\n\tif (getSourcePacket(seqNum)) {\n\t\tortp_debug(\"receive-cluster[%p] packet %d already repaired\", this, seqNum);\n\t\treturn;\n\t}\n\n\tstd::set<uint16_t> seqNumBaseToRepairRow;\n\tstd::set<uint16_t> seqNumBaseToRepairCol;\n\tmFecGraph.getRepairPacketsToRecoverSource(seqNum, seqNumBaseToRepairRow, seqNumBaseToRepairCol);\n\tmRowRepairForDecoding.clear();\n\tmColRepairForDecoding.clear();\n\tfor (uint16_t seqNumRow : seqNumBaseToRepairRow) {\n\t\tif (mRowRepairAll.count(seqNumRow) != 0) {\n\t\t\tmRowRepairForDecoding.emplace_back(mRowRepairAll.at(seqNumRow));\n\t\t}\n\t}\n\tfor (uint16_t seqNumCol : seqNumBaseToRepairCol) {\n\t\tif (mColRepairAll.count(seqNumCol) != 0) {\n\t\t\tmColRepairForDecoding.emplace_back(mColRepairAll.at(seqNumCol));\n\t\t}\n\t}\n\n\trepair2D();\n\treturn;\n}\n\nvoid ReceiveCluster::repair2D() {\n\tint num_recovered_until_this_iteration = 0;\n\tint num_recovered_so_far = 0;\n\n\tdo {\n\t\tnum_recovered_so_far += repair1D(false);\n\t\tnum_recovered_so_far += repair1D(true);\n\n\t\tif (num_recovered_so_far > num_recovered_until_this_iteration) {\n\t\t\tnum_recovered_until_this_iteration = num_recovered_so_far;\n\t\t} else break;\n\n\t} while (1);\n\treturn;\n}\n\nint ReceiveCluster::repair1D(bool interleaved) {\n\tauto repairPackets = (interleaved) ? mColRepairForDecoding : mRowRepairForDecoding;\n\tint repaired = 0;\n\tfor (size_t i = 0; i < repairPackets.size(); i++) {\n\t\trepaired += repairOne(*repairPackets[i]);\n\t}\n\treturn repaired;\n}\n\nint ReceiveCluster::repairOne(FecRepairPacket const &repairPacket) {\n\tstd::vector<uint16_t> seqnumList;\n\tuint16_t seqnumToRepair = 0;\n\n\tint loss = 0;\n\tint i = 0;\n\tBitstring recoveryBs;\n\tseqnumList = repairPacket.createSequenceNumberList();\n\n\twhile (loss <= 1 && (unsigned long)i < seqnumList.size()) {\n\n\t\tstd::shared_ptr<FecSourcePacket> source = getSourcePacket(seqnumList[i]);\n\t\tif (source == nullptr) {\n\t\t\tseqnumToRepair = seqnumList[i];\n\t\t\tloss++;\n\t\t} else {\n\t\t\trecoveryBs.add(source->getBitstring());\n\t\t}\n\t\ti++;\n\t}\n\tif (loss != 1) return 0;\n\n\trecoveryBs.add(repairPacket.extractBitstring());\n\tauto recovery = std::make_shared<FecSourcePacket>(mSession, recoveryBs);\n\trecovery->initPayload(recoveryBs.getLength());\n\trecovery->writeBitstring();\n\trecovery->setSequenceNumber(seqnumToRepair);\n\trecovery->setSsrc(repairPacket.getProtectedSsrc());\n\n\tfor (int i = 0; (unsigned long)i < seqnumList.size(); i++) {\n\t\tif (seqnumList[i] == seqnumToRepair) continue;\n\t\tstd::shared_ptr<FecSourcePacket> sourceP = getSourcePacket(seqnumList[i]);\n\t\trecovery->addPayload(*sourceP);\n\t}\n\trepairAddPacket(*recovery, repairPacket);\n\tmSource.emplace(seqnumToRepair, recovery);\n\tmSourceTimeStamp.emplace(recovery->getBitstring().getTimestamp(), seqnumToRepair);\n\tortp_debug(\"receive-cluster[%p] source packet %u repaired\", this, seqnumToRepair);\n\n\treturn 1;\n}\n\nvoid ReceiveCluster::repairAddPacket(FecSourcePacket &source, FecRepairPacket const &repair) {\n\tuint8_t *rptr = NULL;\n\tsize_t repairSize = repair.repairPayloadStart(&rptr);\n\tsource.addPayload(rptr, repairSize);\n}\n\nvoid ReceiveCluster::reset() {\n\tortp_message(\"receive-cluster[%p] reset packets\", this);\n\tmSourceTimeStamp.clear();\n\tmRepairTimeStamp.clear();\n\tmSource.clear();\n\tmRowRepairAll.clear();\n\tmColRepairAll.clear();\n\tmRowRepairForDecoding.clear();\n\tmColRepairForDecoding.clear();\n\tmFecGraph.reset();\n}"
  },
  {
    "path": "src/fecstream/receive-cluster.h",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef RECIEVE_CLUSTER_H\n#define RECIEVE_CLUSTER_H\n\n#include <map>\n#include <set>\n#include <unordered_map>\n#include <vector>\n\n#include \"fec-packets-connection.h\"\n#include \"fec-params.h\"\n#include \"packet-api.h\"\n\nnamespace ortp {\n\n#ifdef _WIN32\n// Disable C4251 triggered by need to export all stl template classes\n#pragma warning(disable : 4251)\n#endif // ifdef _WIN32\n\nclass FecParamsController;\n\n/** @class ReceiveCluster\n * @brief Class to recover lost RTP packets by applying the flexible FEC.\n *\n * This class receive the source and repair packets, store them for a limited time and trys to recover the\n * missing source packets if asked. The links between the repair packets and the source packets that they protect is\n * made by a FecPacketsConnection object. The repair window defines the maximal time interval where a packet can\n * be kept.\n */\nclass ORTP_PUBLIC ReceiveCluster {\npublic:\n\t/**\n\t * This constructor initializes the object with the RTP session and the repair window.\n\t *\n\t * @param session RTP session of the source and repair packets.\n\t * @param repairWindow Duration of the repair window in microseconds.\n\t */\n\tReceiveCluster(struct _RtpSession *session, uint32_t repairWindow);\n\n\t/**\n\t * Add a new source packet to the cluster. Erase the oldest source packets that are out the repair window. Warning:\n\t * the input source packet is not copied.\n\t *\n\t * @param mp Source packet.\n\t */\n\tvoid add(mblk_t *mp);\n\n\t/**\n\t * Add a new repair packet to the cluster, either on the map of row repair packets or on the map of column repair\n\t * packets. Erase the oldest repair packets that are out the repair window. The FEC graph is updated: the erased\n\t * repair packets are removed and the new one is added.\n\t *\n\t * @param packet Repair packet.\n\t */\n\tvoid add(const std::shared_ptr<FecRepairPacket> &packet);\n\n\t/**\n\t * Return a source packet identified by its sequence number seqnum if it exists in the map mSource, otherwise return\n\t * a null pointer.\n\t *\n\t * @param seqnum Sequence number of the source packet.\n\t */\n\tstd::shared_ptr<FecSourcePacket> getSourcePacket(uint16_t seqnum) const;\n\n\t/**\n\t * Search the source packet with sequence number seqNum. If it doesn't exists in the map of source packet, try to\n\t * recover it with the help of a set of repair packets and the recovering method of flexible FEC. The set of repair\n\t * packets that are related to the missing source packet is identified by the FEC graph. Then the decoding algorithm\n\t * is applied. Each source packet that is recovered then is added to the map of source packets.\n\t *\n\t * @param seqnum Sequence number of the source packet.\n\t */\n\tvoid repair(uint16_t seqNum);\n\n\t/**\n\t * Counter of row repair packets received.\n\t */\n\tuint64_t getRowRepairCpt() const {\n\t\treturn mRowRepairCpt;\n\t};\n\n\t/**\n\t * Counter of column repair packets received.\n\t */\n\tuint64_t getColRepairCpt() const {\n\t\treturn mColRepairCpt;\n\t};\n\n\t/**\n\t * Clear all packets and reset the FEC graph.\n\t */\n\tvoid reset();\n\n\t~ReceiveCluster(){};\n\nprivate:\n\t/**\n\t * Apply the 2D iterative decoding algorithm described in RFC 8627 section 6.3.4 to try to recover the missing\n\t * source packets protceted by the repair packets in mRowRepairForDecoding and mColRepairForDecoding.\n\t */\n\tvoid repair2D();\n\n\t/**\n\t * Apply the 1D decoding algorithms described in RFC 8627 sections 6.3.2 and 6.3.3, on row or columns repair\n\t * packets, given the value of interleaved.\n\t *\n\t * @param interleaved True to apply the interleaved recovery on the column repair packets in mColRepairForDecoding,\n\t * false to apply the non interleaved recovery on the row repair packets in mRowRepairForDecoding.\n\t * @return Number of missing packets that have been recovered.\n\t */\n\tint repair1D(bool interleaved);\n\n\t/**\n\t * Apply the decoding algorithms described in RFC 8627 sections 6.3.2 and 6.3.3, on the repair packet repairPacket.\n\t * If one (and only one) source packet protected by the repair packet misses, its header and payload are recovered.\n\t * The algorithm consists in combining the protceted source and the repair packets with a XOR.\n\t *\n\t * @param interleaved True to apply the interleaved recovery on the column repair packets in mColRepairForDecoding,\n\t * false to apply the non interleaved recovery on the row repair packets in mRowRepairForDecoding.\n\t * @return 1 if a missing packet has been recovered, 0 otherwise.\n\t */\n\tint repairOne(FecRepairPacket const &repairPacket);\n\n\t/**\n\t * Apply the XOR operation between the repair packet and a source packets containing the result of the XOR operation\n\t * between all other source packets protceted by the repair packet.\n\t *\n\t * @param source All other source packets combined with a XOR.\n\t * @param repair Repair packet that protects them.\n\t */\n\tvoid repairAddPacket(FecSourcePacket &source, FecRepairPacket const &repair);\n\n\t/**\n\t * Erase the source packets that are out of the repair window.\n\t */\n\tvoid cleanSource();\n\n\t/**\n\t * Erase the repair packets that are out of the repair window, and update the FEC graph.\n\t */\n\tvoid cleanRepair();\n\n\tRtpSession *mSession;         /**< RTP session containing the streams that receives the source and repair packets.*/\n\tconst uint32_t mRepairWindow; /**< Maximal time interval between the time stamps of the last and the first source\n\t                                 packet received, in microsecond.*/\n\tstd::multimap<uint32_t, uint16_t> mSourceTimeStamp; /**< Sequence numbers of the source packets ordered by their\n\t                                                       time stamps, to handle the repair window.*/\n\tstd::multimap<uint32_t, uint16_t>\n\t    mRepairTimeStamp; /**< Sequence numbers of the repair packets ordered by their\n\t                          time stamps, to handle the repair window. Both row and columns repair packets are added.*/\n\tstd::unordered_map<uint16_t, std::shared_ptr<FecSourcePacket>>\n\t    mSource; /**< Source packets received, in the repair window.*/\n\tstd::unordered_map<uint16_t, std::shared_ptr<FecRepairPacket>>\n\t    mRowRepairAll; /**< Row repair packets received that protect the source packets in mSource.*/\n\tstd::unordered_map<uint16_t, std::shared_ptr<FecRepairPacket>>\n\t    mColRepairAll;      /**< Column repair packets received that protect the source packets in mSource.*/\n\tuint64_t mRowRepairCpt; /**< Counter of the row repair packets received*/\n\tuint64_t mColRepairCpt; /**< Counter of the column repair packets received*/\n\tstd::vector<std::shared_ptr<FecRepairPacket>>\n\t    mRowRepairForDecoding; /**< Row repair packets identified for the recovery of a given source packet.*/\n\tstd::vector<std::shared_ptr<FecRepairPacket>>\n\t    mColRepairForDecoding; /**< Column repair packets identified for the recovery of a given source packet.*/\n\tFecPacketsConnection\n\t    mFecGraph; /**< FEC graph of the connections between the repair packets and the protected source packets*/\n};\n} // namespace ortp\n#endif // RECIEVE_CLUSTER_H"
  },
  {
    "path": "src/jitterctl.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n/***************************************************************************\n *            jitterctl.c\n *\n *  Mon Nov  8 11:53:21 2004\n *  Copyright  2004  Simon MORLAT\n *  Email simon.morlat@linphone.org\n ****************************************************************************/\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include \"ortp/ortp.h\"\n#include \"ortp/payloadtype.h\"\n#include \"ortp/rtpsession.h\"\n#include \"rtpsession_priv.h\"\n#include \"utils.h\"\n#include <math.h>\n\n#define JC_BETA .01\n#define JC_GAMMA (JC_BETA)\n\n#include \"jitterctl.h\"\n\nvoid jitter_control_init(JitterControl *ctl, PayloadType *payload) {\n\tctl->count = 0;\n\tctl->clock_offset_ts = 0;\n\tctl->prev_clock_offset_ts = 0;\n\tctl->jitter = 0;\n\tctl->inter_jitter = 0;\n\tctl->cum_jitter_buffer_count = 0;\n\tctl->cum_jitter_buffer_size = 0;\n\tctl->corrective_slide = 0;\n\n\tctl->clock_rate = 8000;\n\tctl->adapt_refresh_prev_ts = 0;\n\n\tif (payload != NULL) {\n\t\tjitter_control_set_payload(ctl, payload);\n\t}\n}\n\nvoid jitter_control_enable_adaptive(JitterControl *ctl, bool_t val) {\n\tctl->params.adaptive = val;\n}\n\nvoid jitter_control_set_payload(JitterControl *ctl, PayloadType *pt) {\n\tctl->jitt_comp_ts = (int)(((double)ctl->params.nom_size / 1000.0) * (pt->clock_rate));\n\t/*make correction by not less than 10ms */\n\tctl->corrective_step = (int)(0.01 * (float)pt->clock_rate);\n\tctl->adapt_jitt_comp_ts = ctl->jitt_comp_ts;\n\tctl->clock_rate = pt->clock_rate;\n}\n\nvoid jitter_control_dump_stats(JitterControl *ctl) {\n\tortp_message(\"JitterControl:\\n\\tslide=%g,jitter=%g,adapt_jitt_comp_ts=%i,corrective_slide=%i, count=%i\",\n\t             (double)ctl->clock_offset_ts, ctl->jitter, ctl->adapt_jitt_comp_ts, ctl->corrective_slide, ctl->count);\n}\n\n/*the goal of this method is to compute \"corrective_slide\": a timestamp unit'd value to be added\n to recv timestamp to make them reflect the instant they are delivered by the jitter buffer. */\nvoid jitter_control_update_corrective_slide(JitterControl *ctl) {\n\tint tmp;\n\ttmp = (int)(ctl->clock_offset_ts - ctl->prev_clock_offset_ts);\n\tif (tmp > ctl->corrective_step) {\n\t\tctl->corrective_slide += ctl->corrective_step;\n\t\tctl->prev_clock_offset_ts = ctl->clock_offset_ts + ctl->corrective_step;\n\t} else if (tmp < -ctl->corrective_step) {\n\t\tctl->corrective_slide -= ctl->corrective_step;\n\t\tctl->prev_clock_offset_ts = ctl->clock_offset_ts - ctl->corrective_step;\n\t}\n}\n\nvoid jitter_control_update_size(JitterControl *ctl, queue_t *q) {\n\tmblk_t *newest = qlast(q);\n\tmblk_t *oldest = qbegin(q);\n\tuint32_t newest_ts, oldest_ts;\n\tif (newest == NULL) return;\n\tnewest_ts = rtp_get_timestamp(newest);\n\toldest_ts = rtp_get_timestamp(oldest);\n\tctl->cum_jitter_buffer_count++;\n\tctl->cum_jitter_buffer_size += (uint32_t)(newest_ts - oldest_ts);\n}\n\nfloat jitter_control_compute_mean_size(JitterControl *ctl) {\n\tif (ctl->cum_jitter_buffer_count != 0) {\n\t\tdouble tmp = ((double)ctl->cum_jitter_buffer_size) / (double)ctl->cum_jitter_buffer_count;\n\t\tctl->cum_jitter_buffer_size = 0;\n\t\tctl->cum_jitter_buffer_count = 0;\n\t\tctl->jitter_buffer_mean_size = 1000.0f * (float)tmp / (float)ctl->clock_rate;\n\t\treturn ctl->jitter_buffer_mean_size;\n\t}\n\treturn 0;\n}\n\nvoid rtp_session_init_jitter_buffer(RtpSession *session) {\n\tPayloadType *payload = NULL;\n\n\tif (session->rcv.pt != -1) {\n\t\tpayload = rtp_profile_get_payload(session->rcv.profile, session->rcv.pt);\n\t} /*else not set yet */\n\tjitter_control_init(&session->rtp.jittctl, payload);\n}\n\n/**\n *@param session: a RtpSession\n *@param milisec: the time interval in milisec to be jitter compensed.\n *\n * Sets the time interval for which packet are buffered instead of being delivered to the\n * application.\n **/\nvoid rtp_session_set_jitter_compensation(RtpSession *session, int milisec) {\n\tsession->rtp.jittctl.params.min_size = session->rtp.jittctl.params.nom_size = milisec;\n\trtp_session_init_jitter_buffer(session);\n}\n\nvoid rtp_session_enable_adaptive_jitter_compensation(RtpSession *session, bool_t val) {\n\tjitter_control_enable_adaptive(&session->rtp.jittctl, val);\n}\n\nbool_t rtp_session_adaptive_jitter_compensation_enabled(RtpSession *session) {\n\treturn session->rtp.jittctl.params.adaptive;\n}\n\nvoid rtp_session_enable_jitter_buffer(RtpSession *session, bool_t enabled) {\n\tsession->rtp.jittctl.params.enabled = enabled;\n\tsession->flags |= RTP_SESSION_RECV_SYNC;\n}\n\nbool_t rtp_session_jitter_buffer_enabled(const RtpSession *session) {\n\treturn session->rtp.jittctl.params.enabled;\n}\n\nvoid rtp_session_set_jitter_buffer_params(RtpSession *session, const JBParameters *par) {\n\tif (par == &session->rtp.jittctl.params) return;\n\tmemcpy(&session->rtp.jittctl.params, par, sizeof(JBParameters));\n\t// rtp_session_init_jitter_buffer(session);\n\tsession->rtp.jittctl.jb_size_updated = TRUE;\n}\n\nvoid rtp_session_get_jitter_buffer_params(RtpSession *session, JBParameters *par) {\n\tmemcpy(par, &session->rtp.jittctl.params, sizeof(JBParameters));\n}\n\n/*\n The algorithm computes two values:\n    slide: an average of difference between the expected and the socket-received timestamp\n    jitter: an average of the absolute value of the difference between socket-received timestamp and slide.\n    slide is used to make clock-slide detection and correction.\n    jitter is added to the initial jitt_comp_time value. It compensates bursty packets arrival (packets\n    not arriving at regular interval ).\n*/\nvoid jitter_control_new_packet(JitterControl *ctl, uint32_t packet_ts, uint32_t cur_str_ts) {\n\tswitch (ctl->params.buffer_algorithm) {\n\t\tcase OrtpJitterBufferBasic:\n\t\t\tjitter_control_new_packet_basic(ctl, packet_ts, cur_str_ts);\n\t\t\tbreak;\n\t\tcase OrtpJitterBufferRecursiveLeastSquare:\n\t\t\tjitter_control_new_packet_rls(ctl, packet_ts, cur_str_ts);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tortp_fatal(\"No such new packet strategy: %d\", ctl->params.buffer_algorithm);\n\t\t\tbreak;\n\t}\n\tctl->count++;\n}\n\nstatic void jitter_control_update_interarrival_jitter(JitterControl *ctl, int32_t diff) {\n\t/*compute interarrival jitter*/\n\tint32_t delta;\n\tdelta = diff - ctl->olddiff;\n\tctl->inter_jitter = (float)(ctl->inter_jitter + (((float)abs(delta) - ctl->inter_jitter) * (1 / 16.0)));\n\tctl->olddiff = diff;\n}\n\nvoid jitter_control_new_packet_basic(JitterControl *ctl, uint32_t packet_ts, uint32_t cur_str_ts) {\n\tint32_t diff = packet_ts - cur_str_ts;\n\tdouble gap, slide;\n\n\tif (ctl->count == 0) {\n\t\tctl->clock_offset_ts = ctl->prev_clock_offset_ts = diff;\n\t\tslide = (double)diff;\n\t\tctl->olddiff = diff;\n\t\tctl->jitter = 0;\n\t} else {\n\t\tslide = ((double)ctl->clock_offset_ts * (1 - JC_BETA)) + ((double)diff * JC_BETA);\n\t}\n\tgap = (double)diff - slide;\n\tgap = gap < 0 ? -gap : 0; /*compute only for late packets*/\n\tctl->jitter = (float)((ctl->jitter * (1 - JC_GAMMA)) + (gap * JC_GAMMA));\n\tjitter_control_update_interarrival_jitter(ctl, diff);\n\n\tif (ctl->params.adaptive) {\n\t\tif (ctl->count % 50 == 0) {\n\t\t\tctl->adapt_jitt_comp_ts = (int)MAX(ctl->jitt_comp_ts, 2 * ctl->jitter);\n\t\t\t// jitter_control_dump_stats(ctl);\n\t\t}\n\t\tctl->clock_offset_ts = (int32_t)slide;\n\t} else {\n\t\t/*ctl->slide and jitter size are not updated*/\n\t}\n}\n\nstatic bool_t time_for_log(JitterControl *ctl, uint32_t cur_str_ts) {\n\tint32_t elapsed = (int32_t)(cur_str_ts - ctl->last_log_ts);\n\tif (elapsed >= 5 * ctl->clock_rate) {\n\t\tctl->last_log_ts = cur_str_ts;\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n}\n\nstatic uint32_t jitter_control_local_ts_to_remote_ts_rls(JitterControl *ctl, uint32_t local_ts) {\n\treturn (\n\t    uint32_t)((int64_t)(ctl->capped_clock_ratio * (double)(local_ts - ctl->local_ts_start) + ctl->clock_offset_ts));\n}\n\n/**************************** RLS *********************************/\nvoid jitter_control_new_packet_rls(JitterControl *ctl, uint32_t packet_ts, uint32_t cur_str_ts) {\n\tint32_t diff = packet_ts - cur_str_ts;\n\tint deviation;\n\n\tif (ctl->is_diverging) {\n\t\tint32_t elapsed = (int32_t)(cur_str_ts - ctl->diverged_start_ts);\n\t\tif (elapsed >= ctl->clock_rate) {\n\t\t\tortp_error(\"Jitter buffer stays unconverged for one second, reset it.\");\n\t\t\tctl->count = 0;\n\t\t\tctl->is_diverging = FALSE;\n\t\t}\n\t}\n\n\tif (ctl->count == 0) {\n\t\tctl->clock_offset_ts = ctl->prev_clock_offset_ts = (int32_t)packet_ts;\n\t\t/*\n\t\t * Offset compensation. In order to avoid managing the rollover of the uint32_t timestamp, the timestamps passed\n\t\t * to the kalman filter are substracted with their initial value.\n\t\t * This allows a video stream to run 13hours (clockrate: 90 000), which looks at first sight\n\t\t * sufficient for a VoIP application.\n\t\t */\n\t\tctl->local_ts_start = cur_str_ts;\n\t\tctl->remote_ts_start = packet_ts;\n\t\tctl->olddiff = diff;\n\t\tctl->jitter = 0;\n\n\t\tortp_extremum_init(&ctl->max_ts_deviation, (int)(ctl->params.refresh_ms / 1000.f * ctl->clock_rate));\n\t\tortp_extremum_record_max(&ctl->max_ts_deviation, 0, (float)ctl->jitt_comp_ts);\n\n\t\t// clock rates should be the same\n\t\tortp_kalman_rls_init(&ctl->kalman_rls, 1.0, 0.0);\n\t\tctl->capped_clock_ratio = ctl->kalman_rls.m;\n\t}\n\n\t/*Compute the deviation from the value predicted by the kalman filter*/\n\tdeviation = abs((int32_t)(packet_ts - jitter_control_local_ts_to_remote_ts_rls(ctl, cur_str_ts)));\n\n\t/*update the kalman filter*/\n\tortp_kalman_rls_record(&ctl->kalman_rls, cur_str_ts - ctl->local_ts_start, packet_ts - ctl->remote_ts_start);\n\n\tctl->capped_clock_ratio = MAX(.5, MIN(ctl->kalman_rls.m, 2));\n\n\tif (.5f < ctl->kalman_rls.m && ctl->kalman_rls.m < 2.f) {\n\t\t/*realistic clock ratio, the filter is well converged*/\n\t\tctl->clock_offset_ts = (int32_t)((int32_t)ctl->kalman_rls.b + ctl->remote_ts_start);\n\t\tif (ctl->is_diverging) {\n\t\t\tctl->is_diverging = FALSE;\n\t\t}\n\t} else {\n\t\tctl->clock_offset_ts = diff;\n\t\tif (!ctl->is_diverging) {\n\t\t\tctl->is_diverging = TRUE;\n\t\t\tctl->diverged_start_ts = cur_str_ts;\n\t\t}\n\t}\n\n\t/*ortp_message(\"deviation=%g ms\", 1000.0*deviation/(double)ctl->clock_rate);*/\n\n\tjitter_control_update_interarrival_jitter(ctl, diff);\n\tcur_str_ts -= ctl->local_ts_start;\n\n\tif (ctl->params.adaptive || ctl->jb_size_updated) {\n\t\tbool_t max_updated = ortp_extremum_record_max(&ctl->max_ts_deviation, cur_str_ts, (float)deviation);\n\t\tfloat max_deviation =\n\t\t    MAX(ortp_extremum_get_previous(&ctl->max_ts_deviation), ortp_extremum_get_current(&ctl->max_ts_deviation));\n\t\tif (max_updated && max_deviation > ctl->adapt_jitt_comp_ts) {\n\t\t\tctl->adapt_jitt_comp_ts = (int)max_deviation;\n\t\t\tctl->jb_size_updated = TRUE;\n\t\t} else if (max_deviation < ctl->params.ramp_threshold / 100.f * ctl->adapt_jitt_comp_ts) {\n\t\t\t/*Jitter is decreasing. Make a smooth descent to avoid dropping lot of packets*/\n\t\t\tif ((int32_t)(cur_str_ts - ctl->adapt_refresh_prev_ts) >\n\t\t\t    ((ctl->params.ramp_refresh_ms * ctl->clock_rate) / 1000)) {\n\t\t\t\tctl->adapt_jitt_comp_ts -= (ctl->params.ramp_step_ms * ctl->clock_rate) / 1000;\n\t\t\t\tctl->jb_size_updated = TRUE;\n\t\t\t}\n\t\t}\n\t\tif (ctl->jb_size_updated) {\n\t\t\tint min_size_ts = (ctl->params.min_size * ctl->clock_rate) / 1000;\n\t\t\tint max_size_ts = (ctl->params.max_size * ctl->clock_rate) / 1000;\n\t\t\tif (ctl->adapt_jitt_comp_ts < min_size_ts) {\n\t\t\t\tctl->adapt_jitt_comp_ts = min_size_ts;\n\t\t\t} else if (ctl->adapt_jitt_comp_ts > max_size_ts) {\n\t\t\t\tctl->adapt_jitt_comp_ts = max_size_ts;\n\t\t\t}\n\t\t\tctl->adapt_refresh_prev_ts = cur_str_ts;\n\t\t\tctl->jb_size_updated = FALSE;\n\t\t}\n\t}\n\tif (time_for_log(ctl, cur_str_ts)) {\n\t\tortp_message(\"jitter buffer %s: target-size: %f ms, effective-size: %f (min: %i nom: %i, max: %i)\",\n\t\t             ctl->jb_size_updated ? \"updated\" : \"stable\",\n\t\t             ((float)ctl->adapt_jitt_comp_ts / (float)ctl->clock_rate) * 1000.0, ctl->jitter_buffer_mean_size,\n\t\t             ctl->params.min_size, ctl->params.nom_size, ctl->params.max_size);\n\t\tortp_message(\"jitter buffer rls stats: count=%d, clockrate=%i\"\n\t\t             \", offset=%g clock_ratio=%g\"\n\t\t             \", capped_offset=%i capped_clock_ratio=%f\"\n\t\t             \", max_ts_deviation=%f prev_max_ts_deviation=%f\"\n\t\t             \", deviation=%i\"\n\t\t             \", RLS VARIABLES: P[0][0]=%f, P[1][0]=%f, P[0][1]=%f, P[1][1]=%f\",\n\t\t             ctl->count, ctl->clock_rate, ctl->kalman_rls.b, ctl->kalman_rls.m, (int)ctl->clock_offset_ts,\n\t\t             (float)ctl->capped_clock_ratio, ortp_extremum_get_current(&ctl->max_ts_deviation),\n\t\t             ortp_extremum_get_previous(&ctl->max_ts_deviation), deviation, ctl->kalman_rls.P[0][0],\n\t\t             ctl->kalman_rls.P[1][0], ctl->kalman_rls.P[0][1], ctl->kalman_rls.P[1][1]);\n\t}\n}\n\nuint32_t jitter_control_get_compensated_timestamp(JitterControl *obj, uint32_t user_ts) {\n\tuint32_t ret = 0;\n\tswitch (obj->params.buffer_algorithm) {\n\t\tcase OrtpJitterBufferBasic:\n\t\t\tret = (uint32_t)((int64_t)user_ts + obj->clock_offset_ts - (int64_t)obj->adapt_jitt_comp_ts);\n\t\t\tbreak;\n\t\tcase OrtpJitterBufferRecursiveLeastSquare:\n\t\t\tret = jitter_control_local_ts_to_remote_ts_rls(obj, user_ts) - obj->adapt_jitt_comp_ts;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tortp_fatal(\"No such new packet strategy: %d\", obj->params.buffer_algorithm);\n\t\t\tbreak;\n\t}\n\treturn ret;\n}\n"
  },
  {
    "path": "src/jitterctl.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n/***************************************************************************\n *            jitterctl.h\n *\n *  Mon Nov  8 11:53:21 2004\n *  Copyright  2004  Simon MORLAT\n *  Email simon.morlat@linphone.org\n ****************************************************************************/\n\n#ifndef JITTERCTL_H\n#define JITTERCTL_H\n\n#include <ortp/rtpsession.h>\n\nvoid jitter_control_init(JitterControl *ctl, PayloadType *pt);\nvoid jitter_control_enable_adaptive(JitterControl *ctl, bool_t val);\nstatic ORTP_INLINE bool_t jitter_control_adaptive_enabled(JitterControl *ctl) {\n\treturn ctl->params.adaptive;\n}\nvoid jitter_control_set_payload(JitterControl *ctl, PayloadType *pt);\nvoid jitter_control_update_corrective_slide(JitterControl *ctl);\nvoid jitter_control_update_size(JitterControl *ctl, queue_t *q);\nfloat jitter_control_compute_mean_size(JitterControl *ctl);\nvoid jitter_control_new_packet(JitterControl *ctl, uint32_t packet_ts, uint32_t cur_str_ts);\n\nvoid jitter_control_new_packet_basic(JitterControl *ctl, uint32_t packet_ts, uint32_t cur_str_ts);\nvoid jitter_control_new_packet_rls(JitterControl *ctl, uint32_t packet_ts, uint32_t cur_str_ts);\n\nuint32_t jitter_control_get_compensated_timestamp(JitterControl *obj, uint32_t user_ts);\n\n#endif\n"
  },
  {
    "path": "src/kalmanrls.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"ortp/utils.h\"\n\nvoid ortp_kalman_rls_init(OrtpKalmanRLS *rls, double m0, double b0) {\n\tmemset(rls, 0, sizeof(OrtpKalmanRLS));\n\trls->lambda = 1.;\n\trls->P[0][0] = 1e-10;\n\trls->P[1][1] = 1e-1;\n\trls->m = m0;\n\trls->b = b0;\n}\n\nvoid ortp_kalman_rls_record(OrtpKalmanRLS *rls, double xmes, double ymes) {\n\t// P = \ta b\n\t//\t\tc d\n\tdouble a = rls->P[0][0];\n\tdouble b = rls->P[0][1];\n\tdouble c = rls->P[1][0];\n\tdouble d = rls->P[1][1];\n\tdouble e = xmes;\n\tdouble f = 1;\n\n\tdouble estim = rls->m * e + rls->b * f;\n\tdouble deno = rls->lambda + (e * a + f * b) * e + (e * c + f * d) * f;\n\n\t/** This is delta between the measure and our prediction based on previous model values:\n\tthe more accurate the system, the smaller this value.\n\t**/\n\tdouble diff = ymes - estim;\n\n\trls->m = rls->m + diff * (a * e + b * f);\n\trls->b = rls->b + diff * (c * e + d * f);\n\n\trls->P[0][0] = (a - (e * a + f * b) * (e * a + f * c) / deno) * 1.f / rls->lambda;\n\trls->P[1][0] = (b - (e * a + f * b) * (e * b + f * d) / deno) * 1.f / rls->lambda;\n\trls->P[0][1] = (c - (e * c + f * d) * (e * a + f * c) / deno) * 1.f / rls->lambda;\n\trls->P[1][1] = (d - (e * c + f * d) * (e * b + f * d) / deno) * 1.f / rls->lambda;\n}\n"
  },
  {
    "path": "src/logging.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"ortp/logging.h\"\n#include \"ortp/utils.h\"\n\nvoid ortp_set_log_handler(OrtpLogFunc func) {\n\tbctbx_set_log_handler(func);\n}\n\nOrtpLogFunc ortp_get_log_handler(void) {\n\treturn NULL;\n}\n/**\n *@param file a FILE pointer where to output the ortp logs.\n *\n **/\nvoid ortp_set_log_file(FILE *file) {\n\tbctbx_set_log_file(file);\n}\n"
  },
  {
    "path": "src/master",
    "content": "*############################################################\n*#\n*# $Header: /sources/linphone/linphone/oRTP/src/master,v 1.1 2002/02/25 08:41:53 smorlat Exp $\n*#\n*# $Source: /sources/linphone/linphone/oRTP/src/master,v $\n*# $Revision: 1.1 $\n*# $Locker:  $\n*# \n*############################################################\n$VERSION\n1\n$$$\n\n$DRIVER_INSTALL\noRTP\t-1\t-1\n$$$\n\n$LOADABLE\n$$$\n\n$INTERFACE\nbase\n$$$\n\n$DRIVER_DEPENDENCY\n$$$\n\n$TYPE\noRTP\twsio_class\tpseudo\tpmi\t-1\t-1\n$$$\n\n$TUNABLE\n$$$\n"
  },
  {
    "path": "src/nack.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <bctoolbox/defs.h>\n\n#include \"ortp/logging.h\"\n#include \"ortp/nack.h\"\n\n#define DECREASE_JITTER_DELAY 5000\n\nstatic mblk_t *find_packet_with_sequence_number(const queue_t *q, const uint16_t seq_number) {\n\tmblk_t *tmp;\n\n\tfor (tmp = qbegin(q); !qend(q, tmp); tmp = qnext(q, tmp)) {\n\t\tif (rtp_get_seqnumber(tmp) == seq_number) {\n\t\t\treturn tmp;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nstatic void generic_nack_received(const OrtpEventData *evd, OrtpNackContext *ctx) {\n\tif (rtcp_is_RTPFB(evd->packet) && rtcp_RTPFB_get_type(evd->packet) == RTCP_RTPFB_NACK) {\n\t\tRtpTransport *rtpt = NULL;\n\t\trtcp_fb_generic_nack_fci_t *fci;\n\t\tuint16_t pid, blp, seq;\n\t\tmblk_t *lost_msg;\n\n\t\t/* get RTP transport from session */\n\t\trtp_session_get_transports(ctx->session, &rtpt, NULL);\n\n\t\tfci = rtcp_RTPFB_generic_nack_get_fci(evd->packet);\n\t\tpid = rtcp_fb_generic_nack_fci_get_pid(fci);\n\t\tblp = rtcp_fb_generic_nack_fci_get_blp(fci);\n\n\t\tbctbx_mutex_lock(&ctx->sent_packets_mutex);\n\n\t\tlost_msg = find_packet_with_sequence_number(&ctx->sent_packets, pid);\n\t\tif (lost_msg != NULL) {\n\t\t\tmeta_rtp_transport_modifier_inject_packet_to_send(rtpt, ctx->rtp_modifier, lost_msg, 0);\n\t\t\tortp_message(\"OrtpNackContext [%p]: Resending missing packet with seq=%hu\", ctx, pid);\n\t\t} else {\n\t\t\tortp_warning(\"OrtpNackContext [%p]: Cannot find missing packet with seq=%hu\", ctx, pid);\n\t\t}\n\t\t++pid;\n\n\t\tfor (seq = blp; seq != 0; seq >>= 1, ++pid) {\n\t\t\tif (seq & 1) {\n\t\t\t\tlost_msg = find_packet_with_sequence_number(&ctx->sent_packets, pid);\n\t\t\t\tif (lost_msg != NULL) {\n\t\t\t\t\tmeta_rtp_transport_modifier_inject_packet_to_send(rtpt, ctx->rtp_modifier, lost_msg, 0);\n\t\t\t\t\tortp_message(\"OrtpNackContext [%p]: Resending missing packet with seq=%hu\", ctx, pid);\n\t\t\t\t} else {\n\t\t\t\t\tortp_warning(\"OrtpNackContext [%p]: Cannot find missing packet with seq=%hu\", ctx, pid);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tbctbx_mutex_unlock(&ctx->sent_packets_mutex);\n\t}\n}\n\nstatic int ortp_nack_rtp_process_on_send(RtpTransportModifier *t, mblk_t *msg) {\n\tOrtpNackContext *userData = (OrtpNackContext *)t->data;\n\n\tif (rtp_get_version(msg) == 2) {\n\t\tbctbx_mutex_lock(&userData->sent_packets_mutex);\n\n\t\t// Remove the oldest packet if the cache is full\n\t\tif (userData->sent_packets.q_mcount >= userData->max_packets) {\n\t\t\tmblk_t *erase = qbegin(&userData->sent_packets);\n\t\t\tremq(&userData->sent_packets, erase);\n\n\t\t\tif (erase != NULL) freemsg(erase);\n\t\t}\n\n\t\t// Stock the packet before sending it\n\t\tputq(&userData->sent_packets, dupmsg(msg));\n\n\t\t// ortp_message(\"OrtpNackContext [%p]: Stocking packet with pid=%hu (seq=%hu)\", userData,\n\t\t// rtp_get_seqnumber(msg), userData->session->rtp.snd_seq);\n\n\t\tbctbx_mutex_unlock(&userData->sent_packets_mutex);\n\t}\n\n\treturn (int)msgdsize(msg);\n}\n\nstatic int ortp_nack_rtp_process_on_receive(BCTBX_UNUSED(RtpTransportModifier *t), mblk_t *msg) {\n\treturn (int)msgdsize(msg);\n}\n\nstatic int ortp_nack_rtcp_process_on_send(RtpTransportModifier *t, mblk_t *msg) {\n\tRtcpParserContext rtcpctx;\n\n\tconst mblk_t *m_rtcp = rtcp_parser_context_init(&rtcpctx, msg);\n\n\tdo {\n\t\tif (rtcp_is_RTPFB(m_rtcp) && rtcp_RTPFB_get_type(m_rtcp) == RTCP_RTPFB_NACK) {\n\t\t\tOrtpNackContext *userData = (OrtpNackContext *)t->data;\n\t\t\tOrtpEvent *ev;\n\t\t\tOrtpEventData *evd;\n\t\t\tJBParameters jitter_params;\n\t\t\tint rtt = (int)userData->session->rtt;\n\n\t\t\tif (rtt == 0) rtt = 200;\n\n\t\t\trtp_session_get_jitter_buffer_params(userData->session, &jitter_params);\n\n\t\t\tif (userData->min_jitter_before_nack == 0) {\n\t\t\t\t/* We keep the min_size at the first sent NACK to know at which value it has to come back */\n\t\t\t\tuserData->min_jitter_before_nack = jitter_params.min_size;\n\t\t\t}\n\n\t\t\tif (userData->min_jitter_before_nack + rtt != jitter_params.min_size) {\n\t\t\t\tif (userData->min_jitter_before_nack + rtt >= jitter_params.max_size) {\n\t\t\t\t\tjitter_params.min_size = jitter_params.max_size - 20;\n\t\t\t\t} else {\n\t\t\t\t\tjitter_params.min_size = userData->min_jitter_before_nack + rtt;\n\t\t\t\t}\n\n\t\t\t\trtp_session_set_jitter_buffer_params(userData->session, &jitter_params);\n\n\t\t\t\tortp_message(\"OrtpNackContext [%p]: Sending NACK... increasing jitter min size to %dms\", userData,\n\t\t\t\t             jitter_params.min_size);\n\n\t\t\t\t// Send an event that the video jitter has been updated so that we can update the audio too\n\t\t\t\tev = ortp_event_new(ORTP_EVENT_JITTER_UPDATE_FOR_NACK);\n\t\t\t\tevd = ortp_event_get_data(ev);\n\t\t\t\tevd->info.jitter_min_size_for_nack = jitter_params.min_size;\n\t\t\t\trtp_session_dispatch_event(userData->session, ev);\n\t\t\t}\n\n\t\t\t// Start the timer that will decrase the min jitter if no NACK is sent\n\t\t\tuserData->decrease_jitter_timer_running = TRUE;\n\t\t\tuserData->decrease_jitter_timer_start = bctbx_get_cur_time_ms();\n\n\t\t\tbreak;\n\t\t}\n\t} while ((m_rtcp = rtcp_parser_context_next_packet(&rtcpctx)) != NULL);\n\n\trtcp_parser_context_uninit(&rtcpctx);\n\treturn (int)msgdsize(msg);\n}\n\nstatic int ortp_nack_rtcp_process_on_receive(BCTBX_UNUSED(RtpTransportModifier *t), mblk_t *msg) {\n\treturn (int)msgdsize(msg);\n}\n\nstatic void ortp_nack_transport_modifier_destroy(RtpTransportModifier *tp) {\n\tortp_free(tp);\n}\n\nstatic void\nortp_nack_transport_modifier_new(OrtpNackContext *ctx, RtpTransportModifier **rtpt, RtpTransportModifier **rtcpt) {\n\tif (rtpt) {\n\t\t*rtpt = ortp_new0(RtpTransportModifier, 1);\n\t\t(*rtpt)->level = ORTP_RTP_TRANSPORT_MODIFIER_DEFAULT_LEVEL;\n\t\t(*rtpt)->data = ctx; /* back link to get access to the other fields of the OrtpNackContext from the\n\t\t                        RtpTransportModifier structure */\n\t\t(*rtpt)->t_process_on_send = ortp_nack_rtp_process_on_send;\n\t\t(*rtpt)->t_process_on_receive = ortp_nack_rtp_process_on_receive;\n\t\t(*rtpt)->t_destroy = ortp_nack_transport_modifier_destroy;\n\t}\n\n\tif (rtcpt) {\n\t\t*rtcpt = ortp_new0(RtpTransportModifier, 1);\n\t\t(*rtpt)->level = ORTP_RTP_TRANSPORT_MODIFIER_DEFAULT_LEVEL;\n\t\t(*rtcpt)->data = ctx; /* back link to get access to the other fields of the OrtpNackContext from the\n\t\t                         RtpTransportModifier structure */\n\t\t(*rtcpt)->t_process_on_send = ortp_nack_rtcp_process_on_send;\n\t\t(*rtcpt)->t_process_on_receive = ortp_nack_rtcp_process_on_receive;\n\t\t(*rtcpt)->t_destroy = ortp_nack_transport_modifier_destroy;\n\t}\n}\n\nstatic OrtpNackContext *ortp_nack_configure_context(OrtpNackContext *userData) {\n\tRtpTransport *rtpt = NULL, *rtcpt = NULL;\n\tRtpTransportModifier *rtp_modifier, *rtcp_modifier;\n\n\trtp_session_get_transports(userData->session, &rtpt, &rtcpt);\n\n\tortp_nack_transport_modifier_new(userData, &rtp_modifier, &rtcp_modifier);\n\tmeta_rtp_transport_append_modifier(rtpt, rtp_modifier);\n\tmeta_rtp_transport_prepend_modifier(rtcpt, rtcp_modifier);\n\n\tuserData->rtp_modifier = rtp_modifier;\n\tuserData->rtcp_modifier = rtcp_modifier;\n\n\treturn userData;\n}\n\nOrtpNackContext *ortp_nack_context_new(OrtpEvDispatcher *evt) {\n\tOrtpNackContext *userData;\n\n\tuserData = ortp_new0(OrtpNackContext, 1);\n\tuserData->session = evt->session;\n\tuserData->ev_dispatcher = evt;\n\tuserData->max_packets = 100;\n\n\tqinit(&userData->sent_packets);\n\tbctbx_mutex_init(&userData->sent_packets_mutex, NULL);\n\n\trtp_session_enable_avpf_feature(userData->session, ORTP_AVPF_FEATURE_IMMEDIATE_NACK, TRUE);\n\n\tortp_ev_dispatcher_connect(userData->ev_dispatcher, ORTP_EVENT_RTCP_PACKET_RECEIVED, RTCP_RTPFB,\n\t                           (OrtpEvDispatcherCb)generic_nack_received, userData);\n\n\treturn ortp_nack_configure_context(userData);\n}\n\nvoid ortp_nack_context_destroy(OrtpNackContext *ctx) {\n\tRtpTransport *rtpt = NULL, *rtcpt = NULL;\n\n\tortp_ev_dispatcher_disconnect(ctx->ev_dispatcher, ORTP_EVENT_RTCP_PACKET_RECEIVED, RTCP_RTPFB,\n\t                              (OrtpEvDispatcherCb)generic_nack_received);\n\n\trtp_session_enable_avpf_feature(ctx->session, ORTP_AVPF_FEATURE_IMMEDIATE_NACK, FALSE);\n\n\trtp_session_get_transports(ctx->session, &rtpt, &rtcpt);\n\tmeta_rtp_transport_remove_modifier(rtpt, ctx->rtp_modifier);\n\tmeta_rtp_transport_remove_modifier(rtcpt, ctx->rtcp_modifier);\n\tortp_nack_transport_modifier_destroy(ctx->rtp_modifier);\n\tortp_nack_transport_modifier_destroy(ctx->rtcp_modifier);\n\n\tbctbx_mutex_lock(&ctx->sent_packets_mutex);\n\tflushq(&ctx->sent_packets, FLUSHALL);\n\tbctbx_mutex_unlock(&ctx->sent_packets_mutex);\n\n\tbctbx_mutex_destroy(&ctx->sent_packets_mutex);\n\tortp_free(ctx);\n}\n\nvoid ortp_nack_context_set_max_packet(OrtpNackContext *ctx, int max) {\n\tctx->max_packets = max;\n}\n\nvoid ortp_nack_context_process_timer(OrtpNackContext *ctx) {\n\tif (ctx->decrease_jitter_timer_running) {\n\t\tuint64_t current_time = bctbx_get_cur_time_ms();\n\t\tif (current_time - ctx->decrease_jitter_timer_start >= DECREASE_JITTER_DELAY) {\n\t\t\tOrtpEvent *ev;\n\t\t\tOrtpEventData *evd;\n\t\t\tJBParameters jitter_params;\n\n\t\t\tortp_message(\n\t\t\t    \"OrtpNackContext [%p]: No NACK sent in the last %d seconds, decreasing jitter min size to %dms...\", ctx,\n\t\t\t    DECREASE_JITTER_DELAY / 1000, ctx->min_jitter_before_nack);\n\n\t\t\trtp_session_get_jitter_buffer_params(ctx->session, &jitter_params);\n\t\t\tjitter_params.min_size = ctx->min_jitter_before_nack;\n\t\t\trtp_session_set_jitter_buffer_params(ctx->session, &jitter_params);\n\n\t\t\t// Send an event that the video jitter has been updated so that we can update the audio too\n\t\t\tev = ortp_event_new(ORTP_EVENT_JITTER_UPDATE_FOR_NACK);\n\t\t\tevd = ortp_event_get_data(ev);\n\t\t\tevd->info.jitter_min_size_for_nack = jitter_params.min_size;\n\t\t\trtp_session_dispatch_event(ctx->session, ev);\n\n\t\t\tctx->decrease_jitter_timer_running = FALSE;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/netsim.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#ifndef _WIN32\n#include <sys/resource.h>\n#endif\n#include \"ortp/ortp.h\"\n#include \"ortp/rtpsession.h\"\n#include \"rtpsession_priv.h\"\n#include \"utils.h\"\n#include <bctoolbox/port.h>\n\nstatic void rtp_session_schedule_outbound_network_simulator(RtpSession *session, ortpTimeSpec *sleep_until);\n\nstatic OrtpNetworkSimulatorCtx *simulator_ctx_new(void) {\n\tOrtpNetworkSimulatorCtx *ctx = (OrtpNetworkSimulatorCtx *)ortp_malloc0(sizeof(OrtpNetworkSimulatorCtx));\n\tqinit(&ctx->latency_q);\n\tqinit(&ctx->q);\n\tqinit(&ctx->send_q);\n\treturn ctx;\n}\n\nstatic void ortp_network_simulator_dump_stats(OrtpNetworkSimulatorCtx *sim) {\n\tint drop_by_flush = sim->latency_q.q_mcount + sim->q.q_mcount;\n\tif (sim->total_count > 0) {\n\t\tortp_message(\"Network simulation: dump stats. Statistics are:\"\n\t\t             \"%d/%d(%.1f%%, param=%.1f) packets dropped by loss, \"\n\t\t             \"%d/%d(%.1f%%) packets dropped by congestion, \"\n\t\t             \"%d/%d(%.1f%%) packets flushed.\",\n\t\t             sim->drop_by_loss, sim->total_count, sim->drop_by_loss * 100.f / sim->total_count,\n\t\t             sim->params.loss_rate, sim->drop_by_congestion, sim->total_count,\n\t\t             sim->drop_by_congestion * 100.f / sim->total_count, drop_by_flush, sim->total_count,\n\t\t             drop_by_flush * 100.f / sim->total_count);\n\t}\n}\n\nvoid ortp_network_simulator_stop_thread(OrtpNetworkSimulatorCtx *sim) {\n\tif (sim->thread_started) {\n\t\tsim->thread_started = FALSE;\n\t\tortp_thread_join(sim->thread, NULL);\n\t}\n}\n\nvoid ortp_network_simulator_destroy(OrtpNetworkSimulatorCtx *sim) {\n\tortp_network_simulator_dump_stats(sim);\n\tif (sim->thread_started) {\n\t\tortp_network_simulator_stop_thread(sim);\n\t}\n\tflushq(&sim->latency_q, 0);\n\tflushq(&sim->q, 0);\n\tflushq(&sim->send_q, 0);\n\tortp_free(sim);\n}\n\n#ifndef _WIN32\nstatic const char *sched_policy_to_string(int policy) {\n\tswitch (policy) {\n\t\tcase SCHED_OTHER:\n\t\t\treturn \"SCHED_OTHER\";\n\t\tcase SCHED_RR:\n\t\t\treturn \"SCHED_RR\";\n\t\tcase SCHED_FIFO:\n\t\t\treturn \"SCHED_FIFO\";\n\t}\n\treturn \"SCHED_INVALID\";\n}\n#endif\n\nstatic void set_high_prio(void) {\n#ifndef _WIN32\n\tconst char *sched_pref = getenv(\"ORTP_SIMULATOR_SCHED_POLICY\");\n\tint policy = SCHED_OTHER;\n\tstruct sched_param param;\n\tint result = 0;\n\tchar *env_prio_c = NULL;\n\tint min_prio, max_prio, env_prio;\n\n\tif (sched_pref && strcasecmp(sched_pref, \"SCHED_RR\") == 0) {\n\t\tpolicy = SCHED_RR;\n\t} else if (sched_pref && strcasecmp(sched_pref, \"SCHED_FIFO\") == 0) {\n\t\tpolicy = SCHED_FIFO;\n\t}\n\n\tmemset(&param, 0, sizeof(param));\n\n\tmin_prio = sched_get_priority_min(policy);\n\tmax_prio = sched_get_priority_max(policy);\n\tenv_prio_c = getenv(\"ORTP_SIMULATOR_SCHED_PRIO\");\n\n\tenv_prio = (env_prio_c == NULL) ? max_prio : atoi(env_prio_c);\n\n\tenv_prio = MAX(MIN(env_prio, max_prio), min_prio);\n\n\tparam.sched_priority = env_prio;\n\tif ((result = pthread_setschedparam(pthread_self(), policy, &param))) {\n\t\tortp_warning(\"Ortp simulator: set pthread_setschedparam failed: %s\", strerror(result));\n\t} else {\n\t\tortp_message(\"ortp network simulator: sched policy set to %s and priority value (%i)\",\n\t\t             sched_policy_to_string(policy), param.sched_priority);\n\t}\n\t/* The linux kernel has sched_get_priority_max(SCHED_OTHER)=sched_get_priority_max(SCHED_OTHER)=0. As long as we\n\t * can't use SCHED_RR or SCHED_FIFO, the only way to increase priority of a calling thread is to use\n\t * setpriority().*/\n\tif (setpriority(PRIO_PROCESS, 0, -20) == -1) {\n\t\tortp_message(\"Ortp network simulator setpriority() failed: %s, nevermind.\", strerror(errno));\n\t} else {\n\t\tortp_message(\"Ortp network simulator priority increased to maximum.\");\n\t}\n#endif\n}\n\nstatic void *outboud_simulator_thread(void *ctx) {\n\tRtpSession *session = (RtpSession *)ctx;\n\tOrtpNetworkSimulatorCtx *sim = session->net_sim_ctx;\n\tortpTimeSpec sleep_until;\n\tset_high_prio();\n\n\twhile (sim->thread_started) {\n\t\tsleep_until.tv_sec = 0;\n\t\tsleep_until.tv_nsec = 0;\n\t\trtp_session_schedule_outbound_network_simulator(session, &sleep_until);\n\t\tif (sleep_until.tv_sec != 0) ortp_sleep_until(&sleep_until);\n\t\telse bctbx_sleep_ms(1);\n\t}\n\treturn NULL;\n}\n\nconst char *ortp_network_simulator_mode_to_string(OrtpNetworkSimulatorMode mode) {\n\tswitch (mode) {\n\t\tcase OrtpNetworkSimulatorInbound:\n\t\t\treturn \"Inbound\";\n\t\tcase OrtpNetworkSimulatorOutbound:\n\t\t\treturn \"Outbound\";\n\t\tcase OrtpNetworkSimulatorOutboundControlled:\n\t\t\treturn \"OutboundControlled\";\n\t\tcase OrtpNetworkSimulatorInvalid:\n\t\t\treturn \"Invalid\";\n\t}\n\treturn \"invalid\";\n}\n\nOrtpNetworkSimulatorMode ortp_network_simulator_mode_from_string(const char *str) {\n\tif (strcasecmp(str, \"Inbound\") == 0) return OrtpNetworkSimulatorInbound;\n\tif (strcasecmp(str, \"Outbound\") == 0) return OrtpNetworkSimulatorOutbound;\n\tif (strcasecmp(str, \"OutboundControlled\") == 0) return OrtpNetworkSimulatorOutboundControlled;\n\treturn OrtpNetworkSimulatorInvalid;\n}\n\nvoid rtp_session_enable_network_simulation(RtpSession *session, const OrtpNetworkSimulatorParams *params) {\n\tset_high_prio();\n\tOrtpNetworkSimulatorCtx *sim = session->net_sim_ctx;\n\tif (params->enabled) {\n\t\tif (sim == NULL) {\n\t\t\tsim = simulator_ctx_new();\n\t\t} else {\n\t\t\tortp_network_simulator_dump_stats(sim);\n\t\t}\n\t\tsim->drop_by_congestion = sim->drop_by_loss = sim->total_count = 0;\n\t\tsim->params = *params;\n\t\tif (sim->params.jitter_burst_density > 0 && sim->params.jitter_strength > 0 && sim->params.max_bandwidth == 0) {\n\t\t\tsim->params.max_bandwidth = 1024000;\n\t\t\tortp_message(\n\t\t\t    \"Network simulation: jitter requested but max_bandwidth is not set. Using default value of %f bits/s.\",\n\t\t\t    sim->params.max_bandwidth);\n\t\t}\n\t\tif (sim->params.max_bandwidth && sim->params.max_buffer_size == 0) {\n\t\t\tsim->params.max_buffer_size = (int)sim->params.max_bandwidth;\n\t\t\tortp_message(\"Network simulation: Max buffer size not set for RTP session [%p], using [%i]\", session,\n\t\t\t             sim->params.max_buffer_size);\n\t\t}\n\t\tsession->net_sim_ctx = sim;\n\t\tif ((params->mode == OrtpNetworkSimulatorOutbound || params->mode == OrtpNetworkSimulatorOutboundControlled) &&\n\t\t    !sim->thread_started) {\n\t\t\tsim->thread_started = TRUE;\n\t\t\tortp_thread_create(&sim->thread, NULL, outboud_simulator_thread, session);\n\t\t}\n\n\t\tortp_message(\"Network simulation: enabled with the following parameters:\\n\"\n\t\t             \"\\tlatency=%d\\n\"\n\t\t             \"\\tloss_rate=%.1f\\n\"\n\t\t             \"\\tconsecutive_loss_probability=%.1f\\n\"\n\t\t             \"\\tmax_bandwidth=%.1f\\n\"\n\t\t             \"\\tmax_buffer_size=%d\\n\"\n\t\t             \"\\tjitter_density=%.1f\\n\"\n\t\t             \"\\tjitter_strength=%.1f\\n\"\n\t\t             \"\\tmode=%s\",\n\t\t             params->latency, params->loss_rate, params->consecutive_loss_probability, params->max_bandwidth,\n\t\t             params->max_buffer_size, params->jitter_burst_density, params->jitter_strength,\n\t\t             ortp_network_simulator_mode_to_string(params->mode));\n\t} else {\n\t\tif (sim) {\n\t\t\t/* stop thread first: it is using the main_mutex internally and we don't want a deadlock with\n\t\t\t * pthread_join(). */\n\t\t\tortp_network_simulator_stop_thread(sim);\n\t\t}\n\t\tortp_mutex_lock(&session->main_mutex);\n\t\tsession->net_sim_ctx = NULL; /* RtpSession can no longer use it from now on */\n\t\tortp_mutex_unlock(&session->main_mutex);\n\t\tortp_message(\"rtp_session_enable_network_simulation:DISABLING NETWORK SIMULATION\");\n\t\tif (sim != NULL) ortp_network_simulator_destroy(sim);\n\t}\n}\n\nstatic int64_t elapsed_us(struct timeval *tv1, struct timeval *tv2) {\n\treturn ((tv2->tv_sec - tv1->tv_sec) * 1000000LL) + ((tv2->tv_usec - tv1->tv_usec));\n}\n\nstatic mblk_t *simulate_latency(RtpSession *session, mblk_t *input) {\n\tOrtpNetworkSimulatorCtx *sim = session->net_sim_ctx;\n\tstruct timeval current;\n\tmblk_t *output = NULL;\n\tuint32_t current_ts;\n\tbctbx_gettimeofday(&current, NULL);\n\t/*since we must store expiration date in reserved2(32bits) only(reserved1\n\talready used), we need to reduce time stamp to milliseconds only*/\n\tcurrent_ts = 1000 * current.tv_sec + current.tv_usec / 1000;\n\n\t/*queue the packet - store expiration timestamps in reserved fields*/\n\tif (input) {\n\t\tinput->reserved2 = current_ts + sim->params.latency;\n\t\tputq(&sim->latency_q, input);\n\t}\n\n\tif ((output = peekq(&sim->latency_q)) != NULL) {\n\t\tif (TIME_IS_NEWER_THAN(current_ts, output->reserved2)) {\n\t\t\toutput->reserved2 = 0;\n\t\t\tgetq(&sim->latency_q);\n\t\t\t/*return the first dequeued packet*/\n\t\t\treturn output;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nstatic int simulate_jitter_by_bit_budget_reduction(OrtpNetworkSimulatorCtx *sim, int budget_increase) {\n\tunsigned int r = bctbx_random() % 1000;\n\tfloat threshold, score;\n\tint budget_adjust = 0;\n\tuint64_t now = bctbx_get_cur_time_ms();\n\n\tif (sim->last_jitter_event == 0) {\n\t\tsim->last_jitter_event = bctbx_get_cur_time_ms();\n\t}\n\n\tif (sim->in_jitter_event) {\n\t\tthreshold = 100;\n\t\tscore = (float)r;\n\t} else {\n\t\tscore = 1000.0f * (float)r * (now - sim->last_jitter_event) * sim->params.jitter_burst_density * 1e-6f;\n\t\tthreshold = 500;\n\t}\n\tif (score > (int)threshold) {\n\t\tint64_t strength_rand = (int64_t)(sim->params.jitter_strength * (float)(bctbx_random() % 1000));\n\t\tsim->in_jitter_event = TRUE;\n\t\tbudget_adjust = (int)-((int64_t)budget_increase * strength_rand / 1000LL);\n\t\t/*ortp_message(\"jitter in progress... bit_budget_adjustement=%i,\n\t\t * bit_budget=%i\",budget_adjust,sim->bit_budget);*/\n\t} else if (sim->in_jitter_event) {\n\t\t/*ortp_message(\"jitter ended.\");*/\n\t\tsim->in_jitter_event = FALSE;\n\t\tsim->last_jitter_event = bctbx_get_cur_time_ms();\n\t}\n\treturn budget_adjust;\n}\n\nstatic int get_packet_overhead(RtpSession *session, mblk_t *packet) {\n\tbool_t is_rtp_packet = ortp_mblk_get_netsim_is_rtp_flag(packet);\n\tbool_t is_ipv6 = is_rtp_packet ? ortp_stream_is_ipv6(&session->rtp.gs) : ortp_stream_is_ipv6(&session->rtcp.gs);\n\treturn is_ipv6 ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;\n}\n\nstatic mblk_t *simulate_bandwidth_limit_and_jitter(RtpSession *session, mblk_t *input) {\n\tOrtpNetworkSimulatorCtx *sim = session->net_sim_ctx;\n\tstruct timeval current;\n\tint64_t elapsed;\n\tint bits;\n\tint budget_increase;\n\tmblk_t *output = NULL;\n\n\tbctbx_gettimeofday(&current, NULL);\n\n\tif (sim->last_check.tv_sec == 0) {\n\t\tsim->last_check = current;\n\t\tsim->bit_budget = 0;\n\t}\n\t/*update the budget */\n\telapsed = elapsed_us(&sim->last_check, &current);\n\tbudget_increase = (int)((elapsed * (int64_t)sim->params.max_bandwidth) / 1000000LL);\n\tsim->bit_budget += budget_increase;\n\tsim->bit_budget += simulate_jitter_by_bit_budget_reduction(sim, budget_increase);\n\tsim->last_check = current;\n\t/* queue the packet for sending*/\n\tif (input) {\n\t\tputq(&sim->q, input);\n\t\tbits = ((int)msgdsize(input) + get_packet_overhead(session, input)) * 8;\n\t\tsim->qsize += bits;\n\t}\n\t/*flow control*/\n\twhile (sim->qsize >= sim->params.max_buffer_size) {\n\t\t// ortp_message(\"rtp_session_network_simulate(): discarding packets.\");\n\t\toutput = getq(&sim->q);\n\t\tif (output) {\n\t\t\tbits = ((int)msgdsize(output) + get_packet_overhead(session, output)) * 8;\n\t\t\tsim->qsize -= bits;\n\t\t\tsim->drop_by_congestion++;\n\t\t\tfreemsg(output);\n\t\t}\n\t}\n\n\toutput = peekq(&sim->q);\n\n\tif (output) {\n\t\tbits = ((int)msgdsize(output) + get_packet_overhead(session, output)) * 8;\n\t\t/*see if we can output a packet*/\n\t\tif (sim->bit_budget >= bits) {\n\t\t\tsim->bit_budget -= bits;\n\t\t\tsim->qsize -= bits;\n\t\t\toutput = getq(&sim->q);\n\t\t} else output = NULL;\n\t}\n\tif (qempty(&sim->q) && sim->bit_budget >= 0) {\n\t\t/* unused budget is lost...*/\n\t\tsim->last_check.tv_sec = 0;\n\t}\n\treturn output;\n}\n\nstatic mblk_t *simulate_loss_rate(OrtpNetworkSimulatorCtx *net_sim_ctx, mblk_t *input) {\n\tint rrate;\n\tfloat loss_rate = net_sim_ctx->params.loss_rate * 10.0f;\n\n\t/*in order to simulate bursts of dropped packets, take into account a different probability after a loss occurred*/\n\tif (net_sim_ctx->consecutive_drops > 0) {\n\t\tloss_rate = net_sim_ctx->params.consecutive_loss_probability * 1000.0f;\n\t}\n\n\trrate = bctbx_random() % 1000;\n\n\tif (rrate >= loss_rate) {\n\t\tif (net_sim_ctx->consecutive_drops) {\n\t\t\t/*after a burst of lost packets*/\n\t\t\tnet_sim_ctx->drops_to_ignore =\n\t\t\t    net_sim_ctx->consecutive_drops -\n\t\t\t    (int)(((float)net_sim_ctx->consecutive_drops * net_sim_ctx->params.loss_rate) / 100.0f);\n\t\t\tnet_sim_ctx->consecutive_drops = 0;\n\t\t}\n\t\treturn input;\n\t}\n\tif (net_sim_ctx->drops_to_ignore > 0) {\n\t\tnet_sim_ctx->drops_to_ignore--;\n\t\treturn input;\n\t}\n\tif (net_sim_ctx->params.consecutive_loss_probability > 0) {\n\t\tnet_sim_ctx->consecutive_drops++;\n\t}\n\tnet_sim_ctx->drop_by_loss++;\n\tfreemsg(input);\n\treturn NULL;\n}\n\nmblk_t *rtp_session_network_simulate(RtpSession *session, mblk_t *input, bool_t *is_rtp_packet) {\n\tOrtpNetworkSimulatorCtx *sim = session->net_sim_ctx;\n\tmblk_t *om = NULL;\n\n\tom = input;\n\n\t/*while packet is stored in network simulator queue, keep its type in reserved1 space*/\n\tif (om != NULL) {\n\t\tsim->total_count++;\n\t\tortp_mblk_set_netsim_is_rtp_flag(om, *is_rtp_packet);\n\t}\n\n\tif (sim->params.latency > 0) {\n\t\tom = simulate_latency(session, om);\n\t}\n\n\tif ((sim->params.loss_rate > 0) && (om != NULL)) {\n\t\tif (sim->params.rtp_only == TRUE) {\n\t\t\tif (*is_rtp_packet == TRUE) {\n\t\t\t\tom = simulate_loss_rate(sim, om);\n\t\t\t}\n\t\t} else {\n\t\t\tom = simulate_loss_rate(sim, om);\n\t\t}\n\t}\n\n\tif (sim->params.max_bandwidth > 0) {\n\t\tom = simulate_bandwidth_limit_and_jitter(session, om);\n\t}\n\n\t/*finally when releasing the packet from the simulator, reset the flag to default,\n\tsince it is stored in a space used by mediastreamer later (mblk_t.reserved1) */\n\tif (om != NULL) {\n\t\t*is_rtp_packet = ortp_mblk_get_netsim_is_rtp_flag(om);\n\t\tortp_mblk_set_netsim_is_rtp_flag(om, 0);\n\t}\n\treturn om;\n}\n\nstatic mblk_t *rtp_session_netsim_find_next_packet_to_send(RtpSession *session) {\n\tmblk_t *om;\n\tortpTimeSpec min_packet_time = {0, 0};\n\tortpTimeSpec packet_time;\n\tmblk_t *next_packet = NULL;\n\n\tfor (om = qbegin(&session->net_sim_ctx->send_q); !qend(&session->net_sim_ctx->send_q, om);\n\t     om = qnext(&session->net_sim_ctx->send_q, om)) {\n\t\tpacket_time.tv_sec = om->timestamp.tv_sec;\n\t\tpacket_time.tv_nsec = om->timestamp.tv_usec * 1000LL;\n\t\tif (packet_time.tv_sec == 0 && packet_time.tv_nsec == 0) {\n\t\t\t/*this is a packet to drop*/\n\t\t\treturn om;\n\t\t}\n\t\tif (min_packet_time.tv_sec == 0 || ortp_timespec_compare(&packet_time, &min_packet_time) < 0) {\n\t\t\tmin_packet_time = packet_time;\n\t\t\tnext_packet = om;\n\t\t}\n\t}\n\treturn next_packet;\n}\n\nstatic void rtp_session_schedule_outbound_network_simulator(RtpSession *session, ortpTimeSpec *sleep_until) {\n\tmblk_t *om;\n\tint count = 0;\n\tbool_t is_rtp_packet;\n\n\tif (!session->net_sim_ctx) return;\n\n\tif (!session->net_sim_ctx->params.enabled) return;\n\n\tif (session->net_sim_ctx->params.mode == OrtpNetworkSimulatorOutbound) {\n\t\tsleep_until->tv_sec = 0;\n\t\tsleep_until->tv_nsec = 0;\n\t\tortp_mutex_lock(&session->main_mutex);\n\t\twhile ((om = getq(&session->net_sim_ctx->send_q)) != NULL) {\n\t\t\tcount++;\n\t\t\tortp_mutex_unlock(&session->main_mutex);\n\t\t\tis_rtp_packet = ortp_mblk_get_netsim_is_rtp_flag(om); /*it was set by rtp_session_sendto()*/\n\t\t\tom = rtp_session_network_simulate(session, om, &is_rtp_packet);\n\t\t\tif (om) {\n\t\t\t\t_ortp_sendto(rtp_session_get_socket(session, is_rtp_packet), om, 0, (struct sockaddr *)&om->net_addr,\n\t\t\t\t             om->net_addrlen);\n\t\t\t\tfreemsg(om);\n\t\t\t}\n\t\t\tortp_mutex_lock(&session->main_mutex);\n\t\t}\n\t\tortp_mutex_unlock(&session->main_mutex);\n\t\tif (count == 0) {\n\t\t\t/*even if no packets were queued, we have to schedule the simulator*/\n\t\t\tis_rtp_packet = TRUE;\n\t\t\tom = rtp_session_network_simulate(session, NULL, &is_rtp_packet);\n\t\t\tif (om) {\n\t\t\t\t_ortp_sendto(rtp_session_get_socket(session, is_rtp_packet), om, 0, (struct sockaddr *)&om->net_addr,\n\t\t\t\t             om->net_addrlen);\n\t\t\t\tfreemsg(om);\n\t\t\t}\n\t\t}\n\t} else if (session->net_sim_ctx->params.mode == OrtpNetworkSimulatorOutboundControlled) {\n\t\tortpTimeSpec current = {0};\n\t\tortpTimeSpec packet_time;\n\t\tmblk_t *todrop = NULL;\n\n\t\tortp_mutex_lock(&session->main_mutex);\n\t\twhile ((om = rtp_session_netsim_find_next_packet_to_send(session)) != NULL) {\n\t\t\tis_rtp_packet = ortp_mblk_get_netsim_is_rtp_flag(om); /*it was set by rtp_session_sendto()*/\n\t\t\tortp_mutex_unlock(&session->main_mutex);\n\t\t\tif (todrop) {\n\t\t\t\tfreemsg(todrop); /*free the last message while the mutex is not held*/\n\t\t\t\ttodrop = NULL;\n\t\t\t}\n\t\t\tbctbx_get_utc_cur_time(&current);\n\t\t\tpacket_time.tv_sec = om->timestamp.tv_sec;\n\t\t\tpacket_time.tv_nsec = om->timestamp.tv_usec * 1000LL;\n\t\t\tif (is_rtp_packet && om->timestamp.tv_sec == 0 && om->timestamp.tv_usec == 0) {\n\t\t\t\ttodrop = om; /*simulate a packet loss, only RTP packets can be dropped. Timestamp is not set for RTCP\n\t\t\t\t                packets*/\n\t\t\t} else if (ortp_timespec_compare(&packet_time, &current) <= 0) {\n\t\t\t\t/*it is time to send this packet*/\n\n\t\t\t\t_ortp_sendto(is_rtp_packet ? session->rtp.gs.socket : session->rtcp.gs.socket, om, 0,\n\t\t\t\t             (struct sockaddr *)&om->net_addr, om->net_addrlen);\n\t\t\t\ttodrop = om;\n\t\t\t} else {\n\t\t\t\t/*no packet is to be sent yet; set the time at which we want to be called*/\n\t\t\t\t*sleep_until = packet_time;\n\t\t\t\tortp_mutex_lock(&session->main_mutex);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tortp_mutex_lock(&session->main_mutex);\n\t\t\tif (todrop) remq(&session->net_sim_ctx->send_q, todrop); /* remove the message while the mutex is held*/\n\t\t}\n\t\tortp_mutex_unlock(&session->main_mutex);\n\t\tif (todrop) freemsg(todrop);\n\t\tif (sleep_until->tv_sec == 0) {\n\t\t\tbctbx_get_utc_cur_time(&current);\n\t\t\t/*no pending packet in the queue yet, schedule a wake up not too far*/\n\t\t\tsleep_until->tv_sec = current.tv_sec;\n\t\t\tsleep_until->tv_nsec = current.tv_nsec + 1000000LL; /*in 1 ms*/\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/ortp.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include \"ortp/ortp.h\"\n#include \"scheduler.h\"\n#include \"utils.h\"\n#include <inttypes.h>\n\nrtp_stats_t ortp_global_stats;\n\n#ifdef ENABLE_MEMCHECK\nint ortp_allocations = 0;\n#endif\n\nRtpScheduler *__ortp_scheduler;\n\nextern void av_profile_init(RtpProfile *profile);\n\nstatic void init_random_number_generator(void) {\n#ifndef _WIN32\n\tstruct timeval t;\n\tbctbx_gettimeofday(&t, NULL);\n\tsrandom(t.tv_usec + t.tv_sec);\n#endif\n\t/*on windows we're using rand_s, which doesn't require initialization*/\n}\n\n#ifdef _WIN32\nstatic bool_t win32_init_sockets(void) {\n\tWORD wVersionRequested;\n\tWSADATA wsaData;\n\tint i;\n\n\twVersionRequested = MAKEWORD(2, 0);\n\tif ((i = WSAStartup(wVersionRequested, &wsaData)) != 0) {\n\t\tortp_error(\"Unable to initialize windows socket api, reason: %d (%s)\", i, getSocketErrorWithCode(i));\n\t\treturn FALSE;\n\t}\n\treturn TRUE;\n}\n#endif\n\nstatic int ortp_initialized = 0;\n\n/**\n *\tInitialize the oRTP library. You should call this function first before using\n *\toRTP API.\n **/\nvoid ortp_init(void) {\n\tif (ortp_initialized++) return;\n\n#ifdef _WIN32\n\twin32_init_sockets();\n#endif\n\tav_profile_init(&av_profile);\n\tortp_global_stats_reset();\n\tinit_random_number_generator();\n// HAVE_ATOMIC is mandatory but we let it there just in case we want to support other atomic algorithms.\n#ifdef HAVE_ATOMIC\n\tortp_message(\"oRTP-\" ORTP_VERSION \" initialized with Atomic protection.\");\n#else\n\tortp_message(\"oRTP-\" ORTP_VERSION \" initialized.\");\n#endif\n}\n\n/**\n *\tInitialize the oRTP scheduler. You only have to do that if you intend to use the\n *\tscheduled mode of the RtpSession in your application.\n *\n **/\nvoid ortp_scheduler_init(void) {\n\tstatic bool_t initialized = FALSE;\n\tif (initialized) return;\n\tinitialized = TRUE;\n#ifdef __hpux\n\t/* on hpux, we must block sigalrm on the main process, because signal delivery\n\tis ?random?, well, sometimes the SIGALRM goes to both the main thread and the\n\tscheduler thread */\n\tsigset_t set;\n\tsigemptyset(&set);\n\tsigaddset(&set, SIGALRM);\n\tsigprocmask(SIG_BLOCK, &set, NULL);\n#endif /* __hpux */\n\n\t__ortp_scheduler = rtp_scheduler_new();\n\trtp_scheduler_start(__ortp_scheduler);\n}\n\n/**\n * Gracefully uninitialize the library, including shutdowning the scheduler if it was started.\n *\n **/\nvoid ortp_exit(void) {\n\tif (ortp_initialized == 0) {\n\t\tortp_warning(\"ortp_exit() called without prior call to ortp_init(), ignored.\");\n\t\treturn;\n\t}\n\tortp_initialized--;\n\tif (ortp_initialized == 0) {\n\t\tif (__ortp_scheduler != NULL) {\n\t\t\trtp_scheduler_destroy(__ortp_scheduler);\n\t\t\t__ortp_scheduler = NULL;\n\t\t}\n\t}\n}\n\nRtpScheduler *ortp_get_scheduler(void) {\n\tif (__ortp_scheduler == NULL)\n\t\tortp_error(\"Cannot use the scheduled mode: the scheduler is not \"\n\t\t           \"started. Call ortp_scheduler_init() at the begginning of the application.\");\n\treturn __ortp_scheduler;\n}\n\n/**\n * Display global statistics (cumulative for all RtpSession)\n **/\nvoid ortp_global_stats_display(void) {\n\trtp_stats_display(&ortp_global_stats, \"Global statistics\");\n#ifdef ENABLE_MEMCHECK\n\tprintf(\"Unfreed allocations: %i\\n\", ortp_allocations);\n#endif\n}\n\n/**\n * Print RTP statistics.\n **/\nvoid rtp_stats_display(const rtp_stats_t *stats, const char *header) {\n\tortp_log(ORTP_MESSAGE, \"===========================================================\");\n\tortp_log(ORTP_MESSAGE, \"%s\", header);\n\tortp_log(ORTP_MESSAGE, \"-----------------------------------------------------------\");\n\tortp_log(ORTP_MESSAGE, \"sent                                 %10\" PRId64 \" packets\", stats->packet_sent);\n\tortp_log(ORTP_MESSAGE, \"                                     %10\" PRId64 \" duplicated packets\",\n\t         stats->packet_dup_sent);\n\tortp_log(ORTP_MESSAGE, \"                                     %10\" PRId64 \" bytes  \", stats->sent);\n\tortp_log(ORTP_MESSAGE, \"received                             %10\" PRId64 \" packets\", stats->packet_recv);\n\tortp_log(ORTP_MESSAGE, \"                                     %10\" PRId64 \" duplicated packets\",\n\t         stats->packet_dup_recv);\n\tortp_log(ORTP_MESSAGE, \"                                     %10\" PRId64 \" bytes  \", stats->hw_recv);\n\tortp_log(ORTP_MESSAGE, \"incoming delivered to the app        %10\" PRId64 \" bytes  \", stats->recv);\n\tortp_log(ORTP_MESSAGE, \"incoming cumulative lost             %10\" PRId64 \" packets\", stats->cum_packet_loss);\n\tortp_log(ORTP_MESSAGE, \"incoming received too late           %10\" PRId64 \" packets\", stats->outoftime);\n\tortp_log(ORTP_MESSAGE, \"incoming bad formatted               %10\" PRId64 \" packets\", stats->bad);\n\tortp_log(ORTP_MESSAGE, \"incoming discarded (queue overflow)  %10\" PRId64 \" packets\", stats->discarded);\n\tortp_log(ORTP_MESSAGE, \"sent rtcp                            %10\" PRId64 \" packets\", stats->sent_rtcp_packets);\n\tortp_log(ORTP_MESSAGE, \"received rtcp                        %10\" PRId64 \" packets\", stats->recv_rtcp_packets);\n\tortp_log(ORTP_MESSAGE, \"===========================================================\");\n}\n\n/**\n * Print all RTP statistics.\n **/\nvoid rtp_stats_display_all(const rtp_stats_t *stats1, const rtp_stats_t *stats2, const char *header) {\n\tortp_log(ORTP_MESSAGE, \"=================================================================================\");\n\tortp_log(ORTP_MESSAGE, \"%s\", header);\n\tortp_log(ORTP_MESSAGE, \"---------------------------------------------------------------------------------\");\n\tortp_log(ORTP_MESSAGE, \"                                            SRC        FEC    SRC+FEC\");\n\tortp_log(ORTP_MESSAGE, \"---------------------------------------------------------------------------------\");\n\tortp_log(ORTP_MESSAGE, \"sent                                 %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" packets\",\n\t         stats1->packet_sent, stats2->packet_sent, stats1->packet_sent + stats2->packet_sent);\n\tortp_log(ORTP_MESSAGE,\n\t         \"                                     %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" duplicated packets\",\n\t         stats1->packet_dup_sent, stats2->packet_dup_sent, stats1->packet_dup_sent + stats2->packet_dup_sent);\n\tortp_log(ORTP_MESSAGE, \"                                     %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" bytes  \",\n\t         stats1->sent, stats2->sent, stats1->sent + stats2->sent);\n\tortp_log(ORTP_MESSAGE, \"received                             %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" packets\",\n\t         stats1->packet_recv, stats2->packet_recv, stats1->packet_recv + stats2->packet_recv);\n\tortp_log(ORTP_MESSAGE,\n\t         \"                                     %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" duplicated packets\",\n\t         stats1->packet_dup_recv, stats2->packet_dup_recv, stats1->packet_dup_recv + stats2->packet_dup_recv);\n\tortp_log(ORTP_MESSAGE, \"                                     %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" bytes  \",\n\t         stats1->hw_recv, stats2->hw_recv, stats1->hw_recv + stats2->hw_recv);\n\tortp_log(ORTP_MESSAGE, \"incoming delivered to the app        %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" bytes  \",\n\t         stats1->recv, stats2->recv, stats1->recv + stats2->recv);\n\tortp_log(ORTP_MESSAGE, \"incoming cumulative lost             %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" packets\",\n\t         stats1->cum_packet_loss, stats2->cum_packet_loss, stats1->cum_packet_loss + stats2->cum_packet_loss);\n\tortp_log(ORTP_MESSAGE, \"incoming received too late           %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" packets\",\n\t         stats1->outoftime, stats2->outoftime, stats1->outoftime + stats2->outoftime);\n\tortp_log(ORTP_MESSAGE, \"incoming bad formatted               %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" packets\",\n\t         stats1->bad, stats2->bad, stats1->bad + stats2->bad);\n\tortp_log(ORTP_MESSAGE, \"incoming discarded (queue overflow)  %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" packets\",\n\t         stats1->discarded, stats2->discarded, stats1->discarded + stats2->discarded);\n\tortp_log(ORTP_MESSAGE, \"sent rtcp                            %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" packets\",\n\t         stats1->sent_rtcp_packets, stats2->sent_rtcp_packets,\n\t         stats1->sent_rtcp_packets + stats2->sent_rtcp_packets);\n\tortp_log(ORTP_MESSAGE, \"received rtcp                        %10\" PRId64 \" %10\" PRId64 \" %10\" PRId64 \" packets\",\n\t         stats1->recv_rtcp_packets, stats2->recv_rtcp_packets,\n\t         stats1->recv_rtcp_packets + stats2->recv_rtcp_packets);\n\tortp_log(ORTP_MESSAGE, \"=================================================================================\");\n}\n\nvoid ortp_global_stats_reset(void) {\n\tmemset(&ortp_global_stats, 0, sizeof(rtp_stats_t));\n}\n\nrtp_stats_t *ortp_get_global_stats(void) {\n\treturn &ortp_global_stats;\n}\n\nvoid rtp_stats_reset(rtp_stats_t *stats) {\n\tmemset((void *)stats, 0, sizeof(rtp_stats_t));\n}\n\n/**\n * This function give the opportunity to programs to check if the libortp they link to\n * has the minimum version number they need.\n *\n * Returns: true if ortp has a version number greater or equal than the required one.\n **/\nbool_t ortp_min_version_required(int major, int minor, int micro) {\n\treturn ((major * 1000000) + (minor * 1000) + micro) <=\n\t       ((ORTP_MAJOR_VERSION * 1000000) + (ORTP_MINOR_VERSION * 1000) + ORTP_MICRO_VERSION);\n}\n"
  },
  {
    "path": "src/payloadtype.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n\n#include \"ortp/logging.h\"\n#include \"ortp/payloadtype.h\"\n#include \"ortp/str_utils.h\"\n#include <bctoolbox/port.h>\n\nchar *payload_type_get_rtpmap(PayloadType *pt) {\n\tint len = (int)strlen(pt->mime_type) + 15;\n\tchar *rtpmap = (char *)ortp_malloc(len);\n\tif (pt->channels > 0) snprintf(rtpmap, len, \"%s/%i/%i\", pt->mime_type, pt->clock_rate, pt->channels);\n\telse snprintf(rtpmap, len, \"%s/%i\", pt->mime_type, pt->clock_rate);\n\treturn rtpmap;\n}\n\nPayloadType *payload_type_new(void) {\n\tPayloadType *newpayload = (PayloadType *)ortp_new0(PayloadType, 1);\n\tnewpayload->flags |= PAYLOAD_TYPE_ALLOCATED;\n\treturn newpayload;\n}\n\nPayloadType *payload_type_clone(const PayloadType *payload) {\n\tPayloadType *newpayload = (PayloadType *)ortp_new0(PayloadType, 1);\n\tmemcpy(newpayload, payload, sizeof(PayloadType));\n\tnewpayload->mime_type = ortp_strdup(payload->mime_type);\n\tif (payload->recv_fmtp != NULL) {\n\t\tnewpayload->recv_fmtp = ortp_strdup(payload->recv_fmtp);\n\t}\n\tif (payload->send_fmtp != NULL) {\n\t\tnewpayload->send_fmtp = ortp_strdup(payload->send_fmtp);\n\t}\n\tnewpayload->flags |= PAYLOAD_TYPE_ALLOCATED;\n\treturn newpayload;\n}\n\nstatic bool_t canWrite(PayloadType *pt) {\n\tif (!(pt->flags & PAYLOAD_TYPE_ALLOCATED)) {\n\t\tortp_error(\"Cannot change parameters of statically defined payload types: make your\"\n\t\t           \" own copy using payload_type_clone() first.\");\n\t\treturn FALSE;\n\t}\n\treturn TRUE;\n}\n\n/**\n * Sets a recv parameters (fmtp) for the PayloadType.\n * This method is provided for applications using RTP with SDP, but\n * actually the ftmp information is not used for RTP processing.\n **/\nvoid payload_type_set_recv_fmtp(PayloadType *pt, const char *fmtp) {\n\tif (canWrite(pt)) {\n\t\tif (pt->recv_fmtp != NULL) ortp_free(pt->recv_fmtp);\n\t\tif (fmtp != NULL) pt->recv_fmtp = ortp_strdup(fmtp);\n\t\telse pt->recv_fmtp = NULL;\n\t}\n}\n\n/**\n * Sets a send parameters (fmtp) for the PayloadType.\n * This method is provided for applications using RTP with SDP, but\n * actually the ftmp information is not used for RTP processing.\n **/\nvoid payload_type_set_send_fmtp(PayloadType *pt, const char *fmtp) {\n\tif (canWrite(pt)) {\n\t\tif (pt->send_fmtp != NULL) ortp_free(pt->send_fmtp);\n\t\tif (fmtp != NULL) pt->send_fmtp = ortp_strdup(fmtp);\n\t\telse pt->send_fmtp = NULL;\n\t}\n}\n\nvoid payload_type_append_recv_fmtp(PayloadType *pt, const char *fmtp) {\n\tif (canWrite(pt)) {\n\t\tif (pt->recv_fmtp == NULL) pt->recv_fmtp = ortp_strdup(fmtp);\n\t\telse {\n\t\t\tchar *tmp = ortp_strdup_printf(\"%s;%s\", pt->recv_fmtp, fmtp);\n\t\t\tortp_free(pt->recv_fmtp);\n\t\t\tpt->recv_fmtp = tmp;\n\t\t}\n\t}\n}\n\nvoid payload_type_append_send_fmtp(PayloadType *pt, const char *fmtp) {\n\tif (canWrite(pt)) {\n\t\tif (pt->send_fmtp == NULL) pt->send_fmtp = ortp_strdup(fmtp);\n\t\telse {\n\t\t\tchar *tmp = ortp_strdup_printf(\"%s;%s\", pt->send_fmtp, fmtp);\n\t\t\tortp_free(pt->send_fmtp);\n\t\t\tpt->send_fmtp = tmp;\n\t\t}\n\t}\n}\n\nvoid payload_type_set_avpf_params(PayloadType *pt, PayloadTypeAvpfParams params) {\n\tif (canWrite(pt)) {\n\t\tmemcpy(&pt->avpf, &params, sizeof(pt->avpf));\n\t}\n}\n\nbool_t payload_type_is_vbr(const PayloadType *pt) {\n\tif (pt->type == PAYLOAD_VIDEO) return TRUE;\n\treturn !!(pt->flags & PAYLOAD_TYPE_IS_VBR);\n}\n\n/**\n * Frees a PayloadType.\n **/\nvoid payload_type_destroy(PayloadType *pt) {\n\tif (pt->mime_type) ortp_free(pt->mime_type);\n\tif (pt->recv_fmtp) ortp_free(pt->recv_fmtp);\n\tif (pt->send_fmtp) ortp_free(pt->send_fmtp);\n\tortp_free(pt);\n}\n\nstatic const char *find_param_occurence_of(const char *fmtp, const char *param) {\n\tconst char *pos = fmtp;\n\tint param_len = (int)strlen(param);\n\tdo {\n\t\tpos = strstr(pos, param);\n\t\tif (pos) {\n\t\t\t/*check that the occurence found is not a subword of a parameter name*/\n\t\t\tif (pos == fmtp) {\n\t\t\t\tif (pos[param_len] == '=') break; /* found it */\n\t\t\t} else if ((pos[-1] == ';' || pos[-1] == ' ') && pos[param_len] == '=') {\n\t\t\t\tbreak; /* found it */\n\t\t\t}\n\t\t\tpos += strlen(param);\n\t\t}\n\t} while (pos != NULL);\n\treturn pos;\n}\n\nstatic const char *find_last_param_occurence_of(const char *fmtp, const char *param) {\n\tconst char *pos = fmtp;\n\tconst char *lastpos = NULL;\n\tdo {\n\t\tpos = find_param_occurence_of(pos, param);\n\t\tif (pos) {\n\t\t\tlastpos = pos;\n\t\t\tpos += strlen(param);\n\t\t}\n\t} while (pos != NULL);\n\treturn lastpos;\n}\n/**\n * Parses a fmtp string such as \"profile=0;level=10\", finds the value matching\n * parameter param_name, and writes it into result.\n * If a parameter name is found multiple times, only the value of the last occurence is returned.\n * Despite fmtp strings are not used anywhere within oRTP, this function can\n * be useful for people using RTP streams described from SDP.\n * @param fmtp the fmtp line (format parameters)\n * @param param_name the parameter to search for\n * @param result the value given for the parameter (if found)\n * @param result_len the size allocated to hold the result string\n * @return TRUE if the parameter was found, else FALSE.\n **/\nbool_t fmtp_get_value(const char *fmtp, const char *param_name, char *result, size_t result_len) {\n\tconst char *pos = find_last_param_occurence_of(fmtp, param_name);\n\tmemset(result, '\\0', result_len);\n\tif (pos) {\n\t\tconst char *equal = strchr(pos, '=');\n\t\tif (equal) {\n\t\t\tint copied;\n\t\t\tconst char *end = strchr(equal + 1, ';');\n\t\t\tif (end == NULL) end = fmtp + strlen(fmtp); /*assuming this is the last param */\n\t\t\tcopied = MIN((int)(result_len - 1), (int)(end - (equal + 1)));\n\t\t\tstrncpy(result, equal + 1, copied);\n\t\t\tresult[copied] = '\\0';\n\t\t\treturn TRUE;\n\t\t}\n\t}\n\treturn FALSE;\n}\n"
  },
  {
    "path": "src/port.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include \"bctoolbox/charconv.h\"\n#include \"bctoolbox/port.h\"\n#include \"ortp/logging.h\"\n#include \"ortp/port.h\"\n#include \"ortp/str_utils.h\"\n#include \"utils.h\"\n\n#if defined(_WIN32) && !defined(_WIN32_WCE)\n#include <process.h>\n#endif\n\n/*\n * this method is an utility method that calls fnctl() on UNIX or\n * ioctlsocket on Win32.\n * int retrun the result of the system method\n */\nint set_non_blocking_socket(ortp_socket_t sock) {\n#if !defined(_WIN32) && !defined(_WIN32_WCE)\n\treturn fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);\n#else\n\tunsigned long nonBlock = 1;\n\treturn ioctlsocket(sock, FIONBIO, &nonBlock);\n#endif\n}\n\n/*\n * this method is an utility method that calls fnctl() on UNIX or\n * ioctlsocket on Win32.\n * int retrun the result of the system method\n */\nint set_blocking_socket(ortp_socket_t sock) {\n#if !defined(_WIN32) && !defined(_WIN32_WCE)\n\treturn fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) & ~O_NONBLOCK);\n#else\n\tunsigned long nonBlock = 0;\n\treturn ioctlsocket(sock, FIONBIO, &nonBlock);\n#endif\n}\n\n/*\n * this method is an utility method that calls close() on UNIX or\n * closesocket on Win32.\n * int retrun the result of the system method\n */\nint close_socket(ortp_socket_t sock) {\n#if !defined(_WIN32) && !defined(_WIN32_WCE)\n\treturn close(sock);\n#else\n\treturn closesocket(sock);\n#endif\n}\n\n#ifdef _WORKAROUND_MINGW32_BUGS\nchar *WSAAPI gai_strerror(int errnum) {\n\treturn (char *)getSocketErrorWithCode(errnum);\n}\n#endif\n"
  },
  {
    "path": "src/posixtimer.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n\n#include \"ortp/ortp.h\"\n#include \"rtptimer.h\"\n\n#if !defined(_WIN32) && !defined(_WIN32_WCE)\n\n#ifdef __linux__\n#include <sys/select.h>\n#endif\n\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\nstatic struct timeval orig, cur;\nstatic uint32_t posix_timer_time = 0; /*in milisecond */\n\nvoid posix_timer_init(void) {\n\tposix_timer.state = RTP_TIMER_RUNNING;\n\tbctbx_gettimeofday(&orig, NULL);\n\tposix_timer_time = 0;\n}\n\nvoid posix_timer_do(void) {\n\tint diff, time;\n\tstruct timeval tv;\n\tbctbx_gettimeofday(&cur, NULL);\n\ttime = ((cur.tv_usec - orig.tv_usec) / 1000) + ((cur.tv_sec - orig.tv_sec) * 1000);\n\tif ((diff = time - posix_timer_time) > 50) {\n\t\tortp_warning(\"Must catchup %i miliseconds.\", diff);\n\t}\n\twhile ((diff = posix_timer_time - time) > 0) {\n\t\ttv.tv_sec = diff / 1000;\n\t\ttv.tv_usec = (diff % 1000) * 1000;\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\t\t/* this kind of select is not supported on windows */\n\t\tSleep(tv.tv_usec / 1000 + tv.tv_sec * 1000);\n#else\n\t\tselect(0, NULL, NULL, NULL, &tv);\n#endif\n\t\tbctbx_gettimeofday(&cur, NULL);\n\t\ttime = ((cur.tv_usec - orig.tv_usec) / 1000) + ((cur.tv_sec - orig.tv_sec) * 1000);\n\t}\n\tposix_timer_time += POSIXTIMER_INTERVAL / 1000;\n}\n\nvoid posix_timer_uninit(void) {\n\tposix_timer.state = RTP_TIMER_STOPPED;\n}\n\nRtpTimer posix_timer = {0, posix_timer_init, posix_timer_do, posix_timer_uninit, {0, POSIXTIMER_INTERVAL}};\n\n#else //_WIN32\n\n#if defined(ENABLE_MICROSOFT_STORE_APP) || defined(ORTP_WINDOWS_UWP)\n\n#include <mmsystem.h>\n#include <windows.h>\n\nPTP_TIMER timerId;\nHANDLE TimeEvent;\nint late_ticks;\n\nstatic DWORD posix_timer_time;\nstatic DWORD offset_time;\n\n#define TIME_INTERVAL 50\n#define TIME_RESOLUTION 10\n#define TIME_TIMEOUT 100\n\nstatic PTP_TIMER g_timerId = NULL;\nstatic PTP_CLEANUP_GROUP g_cleanupgroup = NULL;\nstatic PTP_POOL g_pool = NULL;\n\nVOID CALLBACK timerCb(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_TIMER Timer) {\n\tif (Timer == g_timerId) {\n\t\tSetEvent(TimeEvent);\n\t\tposix_timer_time += TIME_INTERVAL;\n\t}\n}\n\nvoid win_timer_init(void) {\n\tBOOL bRet = FALSE;\n\tPTP_WORK work = NULL;\n\tFILETIME FileDueTime;\n\tTP_CALLBACK_ENVIRON CallBackEnviron;\n\n\tInitializeThreadpoolEnvironment(&CallBackEnviron);\n\tg_pool = CreateThreadpool(NULL); // Create a custom, dedicated thread pool.\n\tif (NULL == g_pool) {\n\t\tortp_warning(\"CreateThreadpool failed. LastError: %u\\n\", GetLastError());\n\t\treturn;\n\t}\n\tSetThreadpoolThreadMaximum(g_pool, 1);        // The thread pool is made persistent simply by setting\n\tbRet = SetThreadpoolThreadMinimum(g_pool, 1); // both the minimum and maximum threads to 1.\n\tif (FALSE == bRet) {\n\t\tortp_warning(\"SetThreadpoolThreadMinimum failed. LastError: %u\\n\", GetLastError());\n\t\treturn;\n\t}\n\tg_cleanupgroup = CreateThreadpoolCleanupGroup(); // Create a cleanup group for this thread pool.\n\tif (NULL == g_cleanupgroup) {\n\t\tortp_warning(\"CreateThreadpoolCleanupGroup failed. LastError: %u\\n\", GetLastError());\n\t\treturn;\n\t}\n\tSetThreadpoolCallbackPool(&CallBackEnviron, g_pool); // Associate the callback environment with our thread pool.\n\t// Associate the cleanup group with our thread pool. Objects created with the same callback environment as the\n\t// cleanup group become members of the cleanup group.\n\tSetThreadpoolCallbackCleanupGroup(&CallBackEnviron, g_cleanupgroup, NULL);\n\tg_timerId =\n\t    CreateThreadpoolTimer(timerCb, NULL, &CallBackEnviron); // Create a timer with the same callback environment.\n\tif (NULL == g_timerId) {\n\t\tortp_warning(\"CreateThreadpoolTimer failed. LastError: %u\\n\", GetLastError());\n\t\treturn;\n\t}\n\n\tSYSTEMTIME thesystemtime;\n\tGetSystemTime(&thesystemtime);\n\tthesystemtime.wYear++;\n\tSystemTimeToFileTime(&thesystemtime, &FileDueTime);\n\t// ULARGE_INTEGER ulDueTime;\n\t// ulDueTime.QuadPart = (ULONGLONG)604800 * 10 * 1000 * 1000;\n\t// FileDueTime.dwHighDateTime = ulDueTime.HighPart;\n\t// FileDueTime.dwLowDateTime  = ulDueTime.LowPart;\n\tSetThreadpoolTimer(timerId, &FileDueTime, TIME_INTERVAL, 0);\n\tTimeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);\n\tlate_ticks = 0;\n\toffset_time = GetTickCount();\n\tposix_timer_time = 0;\n}\n\nvoid win_timer_do(void) {\n\tif (g_timerId) {\n\t\tDWORD diff;\n\t\t// If timer have expired while we where out of this method\n\t\t// Try to run after lost time.\n\t\tif (late_ticks > 0) {\n\t\t\tlate_ticks--;\n\t\t\tposix_timer_time += TIME_INTERVAL;\n\t\t\treturn;\n\t\t}\n\t\tdiff = GetTickCount() - posix_timer_time - offset_time;\n\t\tif (diff > TIME_INTERVAL && (diff < (1 << 31))) {\n\t\t\tlate_ticks = diff / TIME_INTERVAL;\n\t\t\tortp_warning(\"we must catchup %i ticks.\", late_ticks);\n\t\t\treturn;\n\t\t}\n\t\tWaitForSingleObject(TimeEvent, TIME_TIMEOUT);\n\t\treturn;\n\t}\n}\n\nvoid win_timer_close(void) {\n\tif (g_timerId != NULL) {\n\t\tSetThreadpoolTimer(g_timerId, NULL, 0, 0);\n\t\tCloseThreadpoolTimer(g_timerId);\n\t\tg_timerId = NULL;\n\t}\n\tif (g_cleanupgroup != NULL) { // Clean up the cleanup group members.\n\t\tCloseThreadpoolCleanupGroupMembers(g_cleanupgroup, FALSE, NULL);\n\t\tCloseThreadpoolCleanupGroup(g_cleanupgroup);\n\t\tg_cleanupgroup = NULL;\n\t}\n\tif (g_pool != NULL) { // Clean up the pool.\n\t\tCloseThreadpool(g_pool);\n\t\tg_pool = NULL;\n\t}\n}\n\nRtpTimer toto;\n\nRtpTimer posix_timer = {0, win_timer_init, win_timer_do, win_timer_close, {0, TIME_INTERVAL * 1000}};\n\n#elif defined ORTP_WINDOWS_DESKTOP\n\n#include <mmsystem.h>\n#include <windows.h>\n\nMMRESULT timerId;\nHANDLE TimeEvent;\nint late_ticks;\n\nstatic DWORD posix_timer_time;\nstatic DWORD offset_time;\n\n#define TIME_INTERVAL 50\n#define TIME_RESOLUTION 10\n#define TIME_TIMEOUT 100\n\nvoid CALLBACK timerCb(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) {\n\t// Check timerId\n\tif (timerId == uID) {\n\t\tSetEvent(TimeEvent);\n\t\tposix_timer_time += TIME_INTERVAL;\n\t}\n}\n\nvoid win_timer_init(void) {\n\ttimerId = timeSetEvent(TIME_INTERVAL, 10, timerCb, (DWORD)0, (UINT)(TIME_PERIODIC | TIME_CALLBACK_FUNCTION));\n\tTimeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);\n\n\tlate_ticks = 0;\n\n\toffset_time = GetTickCount();\n\tposix_timer_time = 0;\n}\n\nvoid win_timer_do(void) {\n\tDWORD diff;\n\n\t// If timer have expired while we where out of this method\n\t// Try to run after lost time.\n\tif (late_ticks > 0) {\n\t\tlate_ticks--;\n\t\tposix_timer_time += TIME_INTERVAL;\n\t\treturn;\n\t}\n\n\tdiff = GetTickCount() - posix_timer_time - offset_time;\n\tif (diff > TIME_INTERVAL && (diff < (1 << 31))) {\n\t\tlate_ticks = diff / TIME_INTERVAL;\n\t\tortp_warning(\"we must catchup %i ticks.\", late_ticks);\n\t\treturn;\n\t}\n\n\tWaitForSingleObject(TimeEvent, TIME_TIMEOUT);\n\treturn;\n}\n\nvoid win_timer_close(void) {\n\ttimeKillEvent(timerId);\n}\n\nRtpTimer toto;\n\nRtpTimer posix_timer = {0, win_timer_init, win_timer_do, win_timer_close, {0, TIME_INTERVAL * 1000}};\n\n#elif defined(ORTP_WINDOWS_PHONE)\n\n#include \"winrttimer.h\"\n\nRtpTimer posix_timer = {0, winrt_timer_init, winrt_timer_do, winrt_timer_close, {0, TIME_INTERVAL * 1000}};\n\n#endif\n\n#endif // _WIN32\n"
  },
  {
    "path": "src/rtcp.c",
    "content": "/*\n * Copyright (c) 2004-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/***************************************************************************\n *            rtcp.c\n *\n *  Wed Dec  1 11:45:30 2004\n *  Copyright  2004  Simon Morlat\n *  Email simon dot morlat at linphone dot org\n ****************************************************************************/\n\n#include <bctoolbox/defs.h>\n#include <bctoolbox/port.h>\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include \"jitterctl.h\"\n#include \"ortp/ortp.h\"\n#include \"ortp/rtcp.h\"\n#include \"ortp/rtpsession.h\"\n#include \"rtpsession_priv.h\"\n#include \"utils.h\"\n\n#define rtcp_bye_set_ssrc(b, pos, ssrc) (b)->ssrc[pos] = htonl(ssrc)\n#define rtcp_bye_get_ssrc(b, pos) ntohl((b)->ssrc[pos])\n\nstatic mblk_t *rtcp_create_simple_bye_packet(uint32_t ssrc, const char *reason) {\n\tint packet_size;\n\tint strsize = 0;\n\tint strpadding = 0;\n\tmblk_t *mp;\n\trtcp_bye_t *rtcp;\n\n\tpacket_size = RTCP_BYE_HEADER_SIZE;\n\tif (reason != NULL) {\n\t\tstrsize = (int)MIN(strlen(reason), RTCP_BYE_REASON_MAX_STRING_SIZE);\n\t\tif (strsize > 0) {\n\t\t\tstrpadding = 3 - (strsize % 4);\n\t\t\tpacket_size += 1 + strsize + strpadding;\n\t\t}\n\t}\n\tmp = allocb(packet_size, 0);\n\n\trtcp = (rtcp_bye_t *)mp->b_rptr;\n\trtcp_common_header_init(&rtcp->ch, NULL, RTCP_BYE, 1, packet_size);\n\trtcp->ssrc[0] = htonl(ssrc);\n\tmp->b_wptr += RTCP_BYE_HEADER_SIZE;\n\t/* append the reason if any*/\n\tif (reason != NULL) {\n\t\tconst char pad[] = {0, 0, 0};\n\t\tunsigned char strsize_octet = (unsigned char)strsize;\n\n\t\tappendb(mp, (const char *)&strsize_octet, 1, FALSE);\n\t\tappendb(mp, reason, strsize, FALSE);\n\t\tappendb(mp, pad, strpadding, FALSE);\n\t}\n\treturn mp;\n}\n\nstatic mblk_t *sdes_chunk_new(uint32_t ssrc) {\n\tmblk_t *m = allocb(RTCP_SDES_CHUNK_DEFAULT_SIZE, 0);\n\tsdes_chunk_t *sc = (sdes_chunk_t *)m->b_rptr;\n\tsc->csrc = htonl(ssrc);\n\tm->b_wptr += sizeof(sc->csrc);\n\treturn m;\n}\n\nstatic mblk_t *sdes_chunk_append_item(mblk_t *m, rtcp_sdes_type_t sdes_type, const char *content) {\n\tif (content) {\n\t\tsdes_item_t si;\n\t\tsi.item_type = sdes_type;\n\t\tsi.len = (uint8_t)MIN(strlen(content), RTCP_SDES_MAX_STRING_SIZE);\n\t\tm = appendb(m, (char *)&si, RTCP_SDES_ITEM_HEADER_SIZE, FALSE);\n\t\tm = appendb(m, content, si.len, FALSE);\n\t}\n\treturn m;\n}\n\nstatic mblk_t *sdes_chunk_pad(mblk_t *m) {\n\treturn appendb(m, \"\", 1, TRUE);\n}\n\nstatic mblk_t *sdes_chunk_set_minimal_items(mblk_t *m, const char *cname) {\n\tif (cname == NULL) {\n\t\tcname = \"Unknown\";\n\t}\n\treturn sdes_chunk_append_item(m, RTCP_SDES_CNAME, cname);\n}\n\nstatic mblk_t *sdes_chunk_set_full_items(mblk_t *m,\n                                         const char *cname,\n                                         const char *name,\n                                         const char *email,\n                                         const char *phone,\n                                         const char *loc,\n                                         const char *tool,\n                                         const char *note,\n                                         const char *mid) {\n\tm = sdes_chunk_set_minimal_items(m, cname);\n\tm = sdes_chunk_append_item(m, RTCP_SDES_NAME, name);\n\tm = sdes_chunk_append_item(m, RTCP_SDES_EMAIL, email);\n\tm = sdes_chunk_append_item(m, RTCP_SDES_PHONE, phone);\n\tm = sdes_chunk_append_item(m, RTCP_SDES_LOC, loc);\n\tm = sdes_chunk_append_item(m, RTCP_SDES_TOOL, tool);\n\tm = sdes_chunk_append_item(m, RTCP_SDES_NOTE, note);\n\tm = sdes_chunk_append_item(m, RTCP_SDES_MID, mid);\n\tm = sdes_chunk_pad(m);\n\treturn m;\n}\n\nvoid rtcp_sdes_items_uninit(RtcpSdesItems *items) {\n\tif (items->cname) bctbx_free(items->cname);\n\tif (items->email) bctbx_free(items->email);\n\tif (items->loc) bctbx_free(items->loc);\n\tif (items->name) bctbx_free(items->name);\n\tif (items->note) bctbx_free(items->note);\n\tif (items->phone) bctbx_free(items->phone);\n\tif (items->tool) bctbx_free(items->tool);\n\tmemset(items, 0, sizeof(RtcpSdesItems));\n}\n\nstatic mblk_t *rtp_session_make_sdes(RtpSession *session, bool_t minimal) {\n\tRtcpSdesItems *items = &session->sdes_items;\n\tchar *mid = NULL;\n\tmblk_t *m = NULL;\n\tmblk_t *chunk = sdes_chunk_new(session->snd.ssrc);\n\n\tortp_mutex_lock(&session->main_mutex);\n\tif (strlen(items->cname) > 255) {\n\t\t/*\n\t\t * rfc3550,\n\t\t * 6.5 SDES: Source Description RTCP Packet\n\t\t * ...\n\t\t * Note that the text can be no longer than 255 octets,\n\t\t *\n\t\t * */\n\t\tortp_warning(\"Cname [%s] too long for session [%p]\", items->cname, session);\n\t}\n\n\t/* Add mid to chunck if there is a bundle */\n\tif (session->bundle) {\n\t\tmid = rtp_bundle_get_session_mid(session->bundle, session);\n\t}\n\tif (!minimal) {\n\t\tm = sdes_chunk_set_full_items(chunk, items->cname, items->name, items->email, items->phone, items->loc,\n\t\t                              items->tool, items->note, mid);\n\t} else {\n\t\tm = sdes_chunk_set_minimal_items(chunk, items->cname);\n\t\tif (mid) {\n\t\t\tm = sdes_chunk_append_item(m, RTCP_SDES_MID, mid);\n\t\t}\n\t\tm = sdes_chunk_pad(m);\n\t}\n\tif (mid) bctbx_free(mid);\n\tortp_mutex_unlock(&session->main_mutex);\n\treturn chunk;\n}\n\nstatic void assign_string(char **str, const char *value) {\n\tif (*str) bctbx_free(*str);\n\tif (value) {\n\t\t*str = bctbx_strdup(value);\n\t} else *str = NULL;\n}\n\n/**\n * Set session's SDES item for automatic sending of RTCP compound packets.\n * If some items are not specified, use NULL.\n **/\nvoid rtp_session_set_source_description(RtpSession *session,\n                                        const char *cname,\n                                        const char *name,\n                                        const char *email,\n                                        const char *phone,\n                                        const char *loc,\n                                        const char *tool,\n                                        const char *note) {\n\tRtcpSdesItems *items = &session->sdes_items;\n\tif (strlen(cname) > 255) {\n\t\t/*\n\t\t * rfc3550,\n\t\t * 6.5 SDES: Source Description RTCP Packet\n\t\t * ...\n\t\t * Note that the text can be no longer than 255 octets,\n\t\t *\n\t\t * */\n\t\tortp_warning(\"Cname [%s] too long for session [%p]\", cname, session);\n\t}\n\tortp_mutex_lock(&session->main_mutex);\n\tassign_string(&items->cname, cname);\n\tassign_string(&items->name, name);\n\tassign_string(&items->email, email);\n\tassign_string(&items->phone, phone);\n\tassign_string(&items->loc, loc);\n\tassign_string(&items->tool, tool);\n\tassign_string(&items->note, note);\n\tortp_mutex_unlock(&session->main_mutex);\n}\n\nvoid rtp_session_add_contributing_source(RtpSession *session,\n                                         uint32_t csrc,\n                                         const char *cname,\n                                         const char *name,\n                                         const char *email,\n                                         const char *phone,\n                                         const char *loc,\n                                         const char *tool,\n                                         const char *note) {\n\tchar *mid = NULL;\n\tmblk_t *chunk = sdes_chunk_new(csrc);\n\n\t/* Add mid to chunck if there is a bundle */\n\tif (session->bundle) {\n\t\tmid = rtp_bundle_get_session_mid(session->bundle, session);\n\t}\n\n\tsdes_chunk_set_full_items(chunk, cname, name, email, phone, loc, tool, note, mid);\n\tputq(&session->contributing_sources, chunk);\n\n\tif (mid != NULL) bctbx_free(mid);\n}\n\nvoid rtp_session_remove_contributing_source(RtpSession *session, uint32_t ssrc) {\n\tqueue_t *q = &session->contributing_sources;\n\tmblk_t *tmp;\n\tfor (tmp = qbegin(q); !qend(q, tmp); tmp = qnext(q, tmp)) {\n\t\tuint32_t csrc = sdes_chunk_get_ssrc(tmp);\n\t\tif (csrc == ssrc) {\n\t\t\tremq(q, tmp);\n\t\t\tbreak;\n\t\t}\n\t}\n\ttmp = rtcp_create_simple_bye_packet(ssrc, NULL);\n\trtp_session_rtcp_send(session, tmp);\n}\n\nvoid rtp_session_clear_contributing_sources(RtpSession *session) {\n\tqueue_t *q = &session->contributing_sources;\n\tflushq(q, 0);\n}\n\nvoid rtcp_common_header_init(\n    rtcp_common_header_t *ch, BCTBX_UNUSED(RtpSession *s), int type, int rc, size_t bytes_len) {\n\trtcp_common_header_set_version(ch, 2);\n\trtcp_common_header_set_padbit(ch, 0);\n\trtcp_common_header_set_packet_type(ch, type);\n\trtcp_common_header_set_rc(ch, rc); /* as we don't yet support multi source receiving */\n\trtcp_common_header_set_length(ch, (unsigned short)((bytes_len / 4) - 1));\n}\n\nmblk_t *rtp_session_create_rtcp_sdes_packet(RtpSession *session, bool_t full) {\n\tmblk_t *mp;\n\trtcp_common_header_t *rtcp;\n\tmblk_t *tmp;\n\tmblk_t *m = NULL;\n\tmblk_t *sdes;\n\tqueue_t *q;\n\tint rc = 0;\n\n\tsdes = rtp_session_make_sdes(session, !full);\n\tmp = allocb(sizeof(rtcp_common_header_t), 0);\n\trtcp = (rtcp_common_header_t *)mp->b_wptr;\n\tmp->b_wptr += sizeof(rtcp_common_header_t);\n\tm = concatb(mp, sdes);\n\trc++;\n\n\tif (full == TRUE) {\n\t\tq = &session->contributing_sources;\n\t\tfor (tmp = qbegin(q); !qend(q, tmp); tmp = qnext(q, tmp)) {\n\t\t\tm = concatb(m, dupmsg(tmp));\n\t\t\trc++;\n\t\t}\n\t}\n\trtcp_common_header_init(rtcp, session, RTCP_SDES, rc, msgdsize(mp));\n\n\treturn mp;\n}\n\nstatic void sender_info_init(sender_info_t *info, RtpSession *session) {\n\tstruct timeval tv;\n\tuint64_t ntp;\n\tbctbx_gettimeofday(&tv, NULL);\n\tntp = ortp_timeval_to_ntp(&tv);\n\tinfo->ntp_timestamp_msw = htonl(ntp >> 32);\n\tinfo->ntp_timestamp_lsw = htonl(ntp & 0xFFFFFFFF);\n\tinfo->rtp_timestamp = htonl(session->rtp.snd_last_ts);\n\tinfo->senders_packet_count = (uint32_t)htonl((u_long)session->stats.packet_sent);\n\tinfo->senders_octet_count = (uint32_t)htonl((u_long)session->rtp.sent_payload_bytes);\n\tsession->rtp.last_rtcp_packet_count = (uint32_t)session->stats.packet_sent;\n}\n\nstatic void report_block_init(report_block_t *b, RtpSession *session) {\n\tint packet_loss = 0;\n\tint loss_fraction = 0;\n\tRtpStream *stream = &session->rtp;\n\tuint32_t delay_snc_last_sr = 0;\n\n\t/* compute the statistics */\n\tif (stream->hwrcv_since_last_SR != 0) {\n\t\tuint32_t expected_packets = (uint32_t)(stream->hwrcv_extseq - stream->hwrcv_seq_at_last_SR);\n\n\t\tif (session->flags & RTCP_OVERRIDE_LOST_PACKETS) {\n\t\t\t/* If the test mode is enabled, replace the lost packet field with\n\t\t\tthe test vector value set by rtp_session_rtcp_set_lost_packet_value() */\n\t\t\tpacket_loss = session->lost_packets_test_vector;\n\t\t\t/* The test value is the definite cumulative one, no need to increment\n\t\t\tit each time a packet is sent */\n\t\t\tsession->stats.cum_packet_loss = packet_loss;\n\t\t} else {\n\t\t\t/* Normal mode */\n\t\t\tpacket_loss = (int)(expected_packets - stream->hwrcv_since_last_SR);\n\t\t\tsession->stats.cum_packet_loss += packet_loss;\n\t\t}\n\t\tif (expected_packets > 0 && packet_loss > 0) { /*prevent division by zero and negative loss fraction*/\n\t\t\tloss_fraction = (int)(256 * packet_loss) / expected_packets;\n\t\t\t/*make sure this fits into 8 bit unsigned*/\n\t\t\tif (loss_fraction > 255) loss_fraction = 255;\n\t\t\telse if (loss_fraction < 0) loss_fraction = 0;\n\t\t} else {\n\t\t\tloss_fraction = 0;\n\t\t}\n\t}\n\tortp_debug(\"report_block_init[%p]:\\n\"\n\t           \"\\texpected_packets=%d=%u-%u\\n\"\n\t           \"\\thwrcv_since_last_SR=%u\\n\"\n\t           \"\\tpacket_loss=%d\\n\"\n\t           \"\\tcum_packet_loss=%lld\\n\"\n\t           \"\\tloss_fraction=%f%%\\n\",\n\t           session, stream->hwrcv_extseq - stream->hwrcv_seq_at_last_SR, stream->hwrcv_extseq,\n\t           stream->hwrcv_seq_at_last_SR, stream->hwrcv_since_last_SR, packet_loss,\n\t           (long long)session->stats.cum_packet_loss, loss_fraction / 2.56);\n\n\t/* reset them */\n\tstream->hwrcv_since_last_SR = 0;\n\tstream->hwrcv_seq_at_last_SR = stream->hwrcv_extseq;\n\n\tif (stream->last_rcv_SR_time.tv_sec != 0) {\n\t\tstruct timeval now;\n\t\tdouble delay;\n\t\tbctbx_gettimeofday(&now, NULL);\n\t\tdelay =\n\t\t    (now.tv_sec - stream->last_rcv_SR_time.tv_sec) + ((now.tv_usec - stream->last_rcv_SR_time.tv_usec) * 1e-6);\n\t\tdelay = (delay * 65536);\n\t\tdelay_snc_last_sr = (uint32_t)delay;\n\t}\n\n\tb->ssrc = htonl(session->rcv.ssrc);\n\n\treport_block_set_cum_packet_lost(b, session->stats.cum_packet_loss);\n\treport_block_set_fraction_lost(b, loss_fraction);\n\n\tif (session->flags & RTCP_OVERRIDE_JITTER) {\n\t\t/* If the test mode is enabled, replace the interarrival jitter field with the test vector value set by\n\t\t * rtp_session_rtcp_set_jitter_value() */\n\t\tb->interarrival_jitter = htonl(session->interarrival_jitter_test_vector);\n\t} else {\n\t\t/* Normal mode */\n\t\tb->interarrival_jitter = htonl((uint32_t)stream->jittctl.inter_jitter);\n\t}\n\tb->ext_high_seq_num_rec = htonl(stream->hwrcv_extseq);\n\tb->delay_snc_last_sr = htonl(delay_snc_last_sr);\n\tif (session->flags & RTCP_OVERRIDE_DELAY) {\n\t\t/* If the test mode is enabled, modifies the returned ts (LSR) so it matches the value of the delay test value\n\t\t */\n\t\t/* refer to the rtp_session_rtcp_set_delay_value() documentation for further explanations */\n\t\tdouble new_ts = ((double)stream->last_rcv_SR_time.tv_sec + (double)stream->last_rcv_SR_time.tv_usec * 1e-6) -\n\t\t                ((double)session->delay_test_vector / 1000.0);\n\t\tuint32_t new_ts2;\n\n\t\t/* Converting the time format in RFC3550 (par. 4) format */\n\t\tnew_ts += 2208988800.0; /* 2208988800 is the number of seconds from 1900 to 1970 (January 1, Oh TU) */\n\t\tnew_ts = 65536.0 * new_ts;\n\t\t/* This non-elegant way of coding fits with the gcc and the icc compilers */\n\t\tnew_ts2 = (uint32_t)((uint64_t)new_ts & 0xffffffff);\n\t\tb->lsr = htonl(new_ts2);\n\t} else {\n\t\t/* Normal mode */\n\t\tb->lsr = htonl(stream->last_rcv_SR_ts);\n\t}\n}\n\nstatic void extended_statistics(RtpSession *session, BCTBX_UNUSED(report_block_t *rb)) {\n\t/* the jitter raw value is kept in stream clock units */\n\tuint32_t jitter = (uint32_t)session->rtp.jittctl.inter_jitter;\n\tsession->rtp.jitter_stats.sum_jitter += jitter;\n\tsession->rtp.jitter_stats.jitter = jitter;\n\t/* stores the biggest jitter for that session and its date (in millisecond) since Epoch */\n\tif (jitter > session->rtp.jitter_stats.max_jitter) {\n\t\tstruct timeval now;\n\n\t\tsession->rtp.jitter_stats.max_jitter = jitter;\n\n\t\tbctbx_gettimeofday(&now, NULL);\n\t\tsession->rtp.jitter_stats.max_jitter_ts = (now.tv_sec * 1000LL) + (now.tv_usec / 1000LL);\n\t}\n\t/* compute mean jitter buffer size */\n\tsession->rtp.jitter_stats.jitter_buffer_size_ms = jitter_control_compute_mean_size(&session->rtp.jittctl);\n}\n\nstatic size_t rtcp_sr_init(RtpSession *session, uint8_t *buf, size_t size) {\n\trtcp_sr_t *sr = (rtcp_sr_t *)buf;\n\tint rr = (session->stats.packet_recv > 0);\n\tsize_t sr_size = sizeof(rtcp_sr_t) - sizeof(report_block_t) + (rr * sizeof(report_block_t));\n\tif (size < sr_size) return 0;\n\trtcp_common_header_init(&sr->ch, session, RTCP_SR, rr, sr_size);\n\tsr->ssrc = htonl(session->snd.ssrc);\n\tsender_info_init(&sr->si, session);\n\t/*only include a report block if packets were received*/\n\tif (rr) {\n\t\treport_block_init(&sr->rb[0], session);\n\t\textended_statistics(session, &sr->rb[0]);\n\t}\n\treturn sr_size;\n}\n\nstatic size_t rtcp_rr_init(RtpSession *session, uint8_t *buf, size_t size) {\n\trtcp_rr_t *rr = (rtcp_rr_t *)buf;\n\tif (size < sizeof(rtcp_rr_t)) return 0;\n\trtcp_common_header_init(&rr->ch, session, RTCP_RR, 1, sizeof(rtcp_rr_t));\n\trr->ssrc = htonl(session->snd.ssrc);\n\treport_block_init(&rr->rb[0], session);\n\textended_statistics(session, &rr->rb[0]);\n\treturn sizeof(rtcp_rr_t);\n}\n\nstatic size_t rtcp_app_init(RtpSession *session, uint8_t *buf, uint8_t subtype, const char *name, size_t size) {\n\trtcp_app_t *app = (rtcp_app_t *)buf;\n\tif (size < sizeof(rtcp_app_t)) return 0;\n\trtcp_common_header_init(&app->ch, session, RTCP_APP, subtype, size);\n\tapp->ssrc = htonl(session->snd.ssrc);\n\tmemset(app->name, 0, sizeof(app->name));\n\tmemcpy(app->name, name, sizeof(app->name));\n\treturn sizeof(rtcp_app_t);\n}\n\nstatic mblk_t *make_rr(RtpSession *session) {\n\tmblk_t *cm = allocb(sizeof(rtcp_sr_t), 0);\n\tcm->b_wptr += rtcp_rr_init(session, cm->b_wptr, sizeof(rtcp_rr_t));\n\treturn cm;\n}\n\nstatic mblk_t *make_sr(RtpSession *session) {\n\tmblk_t *cm = allocb(sizeof(rtcp_sr_t), 0);\n\tcm->b_wptr += rtcp_sr_init(session, cm->b_wptr, sizeof(rtcp_sr_t));\n\treturn cm;\n}\n\nstatic mblk_t *append_sdes(RtpSession *session, mblk_t *m, bool_t full) {\n\tmblk_t *sdes = NULL;\n\n\tsdes = rtp_session_create_rtcp_sdes_packet(session, full);\n\treturn concatb(m, sdes);\n}\n\nstatic void notify_sent_rtcp(RtpSession *session, mblk_t *rtcp) {\n\tif (session->eventqs != NULL) {\n\t\tOrtpEvent *ev;\n\t\tOrtpEventData *evd;\n\t\tev = ortp_event_new(ORTP_EVENT_RTCP_PACKET_EMITTED);\n\t\tevd = ortp_event_get_data(ev);\n\t\tevd->packet = dupmsg(rtcp);\n\t\tmsgpullup(evd->packet, -1);\n\t\trtp_session_dispatch_event(session, ev);\n\t}\n}\n\nstatic void append_xr_packets(RtpSession *session, mblk_t *m) {\n\tif (session->rtcp.xr_conf.rcvr_rtt_mode != OrtpRtcpXrRcvrRttNone) {\n\t\tconcatb(m, make_xr_rcvr_rtt(session));\n\t}\n\tif (session->rtcp.rtcp_xr_dlrr_to_send == TRUE) {\n\t\tconcatb(m, make_xr_dlrr(session));\n\t\tsession->rtcp.rtcp_xr_dlrr_to_send = FALSE;\n\t}\n\tif (session->rtcp.xr_conf.stat_summary_enabled == TRUE) {\n\t\tconcatb(m, make_xr_stat_summary(session));\n\t}\n\tif (session->rtcp.xr_conf.voip_metrics_enabled == TRUE) {\n\t\tconcatb(m, make_xr_voip_metrics(session));\n\t}\n}\n\nstatic void append_fb_packets(RtpSession *session, mblk_t *m) {\n\tif (session->rtcp.send_algo.fb_packets != NULL) {\n\t\tconcatb(m, session->rtcp.send_algo.fb_packets);\n\t\tsession->rtcp.send_algo.fb_packets = NULL;\n\t}\n\n\t/* Repeat TMMBR packets until they are acknowledged with a TMMBN unless a TMMBN is being sent. */\n\tif (rtp_session_avpf_feature_enabled(session, ORTP_AVPF_FEATURE_TMMBR) && (session->rtcp.tmmbr_info.sent != NULL) &&\n\t    (session->rtcp.send_algo.tmmbr_scheduled != TRUE) && (session->rtcp.send_algo.tmmbn_scheduled != TRUE)) {\n\t\tconcatb(m, copymsg(session->rtcp.tmmbr_info.sent));\n\t}\n\n\tsession->rtcp.send_algo.tmmbr_scheduled = FALSE;\n\tsession->rtcp.send_algo.tmmbn_scheduled = FALSE;\n\n\t/* Repeat goog-remb packet at an interval as it is what WebRTC is doing */\n\tif (rtp_session_avpf_feature_enabled(session, ORTP_AVPF_FEATURE_GOOG_REMB) &&\n\t    session->rtcp.goog_remb_info.sent != NULL) {\n\t\tuint64_t time = bctbx_get_cur_time_ms();\n\n\t\tif (session->rtcp.send_algo.goog_remb_scheduled == FALSE) {\n\t\t\tif (time - session->rtcp.goog_remb_info.sent_time > 1000) {\n\t\t\t\tconcatb(m, copymsg(session->rtcp.goog_remb_info.sent));\n\t\t\t\tsession->rtcp.goog_remb_info.sent_time = time;\n\t\t\t}\n\t\t} else {\n\t\t\t/* If goog_remb_scheduled is TRUE we are actually sending a goog-remb so update the sent time */\n\t\t\tsession->rtcp.goog_remb_info.sent_time = time;\n\t\t}\n\t}\n\n\tsession->rtcp.send_algo.goog_remb_scheduled = FALSE;\n}\n\nstatic void rtp_session_create_and_send_rtcp_packet(RtpSession *session, bool_t full) {\n\tmblk_t *m = NULL;\n\tbool_t is_sr = FALSE;\n\n\tif (session->rtp.last_rtcp_packet_count < session->stats.packet_sent) {\n\t\tm = make_sr(session);\n\t\tsession->rtp.last_rtcp_packet_count = (uint32_t)session->stats.packet_sent;\n\t\tis_sr = TRUE;\n\t} else if (session->stats.packet_recv > 0) {\n\t\t/* Don't send RR when no packet are received yet */\n\t\tm = make_rr(session);\n\t\tis_sr = FALSE;\n\t}\n\tif (m != NULL) {\n\t\tappend_sdes(session, m, full);\n\t\tif ((full == TRUE) && (session->rtcp.xr_conf.enabled == TRUE)) {\n\t\t\tappend_xr_packets(session, m);\n\t\t}\n\t\tif (rtp_session_avpf_enabled(session) == TRUE) {\n\t\t\tappend_fb_packets(session, m);\n\t\t}\n\t\t/* Send the compound packet */\n\t\tnotify_sent_rtcp(session, m);\n\t\tortp_message(\"Sending RTCP %s compound message on session [%p].\", (is_sr ? \"SR\" : \"RR\"), session);\n\t\tsession->stats.sent_rtcp_packets++;\n\t\trtp_session_rtcp_send(session, m);\n\t}\n}\n\nstatic float rtcp_rand(float t) {\n\treturn t * ((bctbx_random() / ((float)0xffffffff)) + 0.5f);\n}\n\n/**\n * This is a simplified version with this limit of the algorithm described in\n * the appendix A.7 of RFC3550.\n */\nvoid compute_rtcp_interval(RtpSession *session) {\n\tfloat t;\n\tfloat rtcp_min_time;\n\tfloat rtcp_bw;\n\n\tif (session->target_upload_bandwidth == 0) return;\n\n\t/* Compute target RTCP bandwidth in bits/s. */\n\trtcp_bw = 0.05f * session->target_upload_bandwidth;\n\n\tif (rtp_session_avpf_enabled(session) == TRUE) {\n\t\tsession->rtcp.send_algo.T_rr_interval = rtp_session_get_avpf_rr_interval(session);\n\t\trtcp_min_time = (float)session->rtcp.send_algo.Tmin;\n\t} else {\n\t\trtcp_min_time = (float)session->rtcp.send_algo.T_rr_interval;\n\t\tif (session->rtcp.send_algo.initial == TRUE) {\n\t\t\trtcp_min_time /= 2.;\n\t\t}\n\t}\n\n\tt = ((session->rtcp.send_algo.avg_rtcp_size * 8 * 2) / rtcp_bw) * 1000;\n\tif (t < rtcp_min_time) t = rtcp_min_time;\n\tt = rtcp_rand(t);\n\tt = t / (2.71828f - 1.5f); /* Compensation */\n\tsession->rtcp.send_algo.T_rr = (uint32_t)t;\n}\n\nvoid rtp_session_update_avg_rtcp_size(RtpSession *session, int bytes) {\n\tint overhead = (ortp_stream_is_ipv6(&session->rtcp.gs) == TRUE) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;\n\tint size = bytes + overhead;\n\tsession->rtcp.send_algo.avg_rtcp_size = ((size + (15 * session->rtcp.send_algo.avg_rtcp_size)) / 16.f);\n}\n\nstatic void rtp_session_schedule_first_rtcp_send(RtpSession *session) {\n\tuint64_t tc;\n\tsize_t overhead;\n\tsize_t report_size;\n\tsize_t sdes_size;\n\tsize_t xr_size = 0;\n\tOrtpRtcpSendAlgorithm *sa = &session->rtcp.send_algo;\n\n\tif ((session->rtcp.enabled == FALSE) || (session->target_upload_bandwidth == 0) || (sa->initialized == TRUE))\n\t\treturn;\n\n\toverhead = (ortp_stream_is_ipv6(&session->rtcp.gs) == TRUE) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;\n\tsdes_size = 0; /*FIXME: should be adapted to SDES size */\n\tswitch (session->mode) {\n\t\tcase RTP_SESSION_RECVONLY:\n\t\t\treport_size = sizeof(rtcp_rr_t);\n\t\t\tbreak;\n\t\tcase RTP_SESSION_SENDONLY:\n\t\t\treport_size = sizeof(rtcp_sr_t) - sizeof(report_block_t);\n\t\t\tbreak;\n\t\tcase RTP_SESSION_SENDRECV:\n\t\tdefault:\n\t\t\treport_size = sizeof(rtcp_sr_t);\n\t\t\tbreak;\n\t}\n\tif (session->rtcp.xr_conf.enabled == TRUE) {\n\t\tif (session->rtcp.xr_conf.rcvr_rtt_mode != OrtpRtcpXrRcvrRttNone)\n\t\t\txr_size += sizeof(rtcp_xr_header_t) + sizeof(rtcp_xr_rcvr_rtt_report_block_t);\n\t\tif (session->rtcp.xr_conf.stat_summary_enabled == TRUE)\n\t\t\txr_size += sizeof(rtcp_xr_header_t) + sizeof(rtcp_xr_stat_summary_report_block_t);\n\t\tif (session->rtcp.xr_conf.voip_metrics_enabled == TRUE)\n\t\t\txr_size += sizeof(rtcp_xr_header_t) + sizeof(rtcp_xr_voip_metrics_report_block_t);\n\t}\n\tsa->avg_rtcp_size = (float)(overhead + report_size + sdes_size + xr_size);\n\tsa->initialized = TRUE;\n\n\ttc = bctbx_get_cur_time_ms();\n\tcompute_rtcp_interval(session);\n\tif (sa->T_rr > 0) sa->tn = tc + sa->T_rr;\n\tsa->tp = tc;\n\tsa->t_rr_last = tc;\n\tsa->Tmin = 0;\n}\n\nstatic void rtp_session_reschedule(RtpSession *session, uint64_t tc) {\n\tOrtpRtcpSendAlgorithm *sa = &session->rtcp.send_algo;\n\tif (rtp_session_avpf_enabled(session) == TRUE) {\n\t\tsa->tp = tc;\n\t\tsa->tn = tc + sa->T_rr;\n\t}\n}\n\nvoid rtp_session_send_regular_rtcp_packet_and_reschedule(RtpSession *session, uint64_t tc) {\n\tOrtpRtcpSendAlgorithm *sa = &session->rtcp.send_algo;\n\trtp_session_create_and_send_rtcp_packet(session, TRUE);\n\tsa->tp = tc;\n\tsa->t_rr_last = sa->tn;\n\tcompute_rtcp_interval(session);\n\tsa->tn = tc + sa->T_rr;\n\tsa->initial = FALSE;\n}\n\nvoid rtp_session_send_fb_rtcp_packet_and_reschedule(RtpSession *session) {\n\tuint64_t previous_tn;\n\tOrtpRtcpSendAlgorithm *sa = &session->rtcp.send_algo;\n\trtp_session_create_and_send_rtcp_packet(session, FALSE);\n\tsa->allow_early = FALSE;\n\tprevious_tn = sa->tn;\n\tsa->tn = sa->tp + 2 * sa->T_rr;\n\tsa->tp = previous_tn;\n}\n\nvoid rtp_session_run_rtcp_send_scheduler(RtpSession *session) {\n\tuint64_t tc = bctbx_get_cur_time_ms();\n\tOrtpRtcpSendAlgorithm *sa = &session->rtcp.send_algo;\n\n\tif (tc >= sa->tn) {\n\t\tcompute_rtcp_interval(session);\n\t\tsa->tn = sa->tp + sa->T_rr;\n\t\tif (tc >= sa->tn) {\n\t\t\tif (sa->t_rr_last == 0) {\n\t\t\t\trtp_session_schedule_first_rtcp_send(session);\n\t\t\t} else {\n\t\t\t\tif (sa->T_rr_interval != 0) {\n\t\t\t\t\tsa->T_rr_current_interval = (uint32_t)rtcp_rand((float)sa->T_rr_interval);\n\t\t\t\t} else {\n\t\t\t\t\tsa->T_rr_current_interval = 0;\n\t\t\t\t}\n\t\t\t\tif (sa->tn >= (sa->t_rr_last + sa->T_rr_current_interval)) {\n\t\t\t\t\trtp_session_send_regular_rtcp_packet_and_reschedule(session, tc);\n\t\t\t\t} else if (rtp_session_has_fb_packets_to_send(session) == TRUE) {\n\t\t\t\t\trtp_session_send_fb_rtcp_packet_and_reschedule(session);\n\t\t\t\t} else {\n\t\t\t\t\trtp_session_reschedule(session, tc);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid rtp_session_rtcp_process_send(RtpSession *session) {\n\trtp_session_run_rtcp_send_scheduler(session);\n}\n\nvoid rtp_session_rtcp_process_recv(RtpSession *session) {\n\trtp_session_run_rtcp_send_scheduler(session);\n}\n\nvoid rtp_session_send_rtcp_APP(\n    RtpSession *session, uint8_t subtype, const char *name, const uint8_t *data, int datalen) {\n\tmblk_t *h = allocb(sizeof(rtcp_app_t), 0);\n\tmblk_t *d;\n\th->b_wptr += rtcp_app_init(session, h->b_wptr, subtype, name, datalen + sizeof(rtcp_app_t));\n\td = esballoc((uint8_t *)data, datalen, 0, NULL);\n\td->b_wptr += datalen;\n\th->b_cont = d;\n\trtp_session_rtcp_send(session, h);\n}\n\n/**\n * Sends a RTCP bye packet.\n *@param session RtpSession\n *@param reason the reason phrase.\n **/\nint rtp_session_bye(RtpSession *session, const char *reason) {\n\tmblk_t *cm;\n\tmblk_t *sdes = NULL;\n\tmblk_t *bye = NULL;\n\tint ret;\n\n\t/* Make a BYE packet (will be on the end of the compund packet). */\n\tbye = rtcp_create_simple_bye_packet(session->snd.ssrc, reason);\n\n\t/* SR or RR is determined by the fact whether stream was sent*/\n\tif (session->stats.packet_sent > 0) {\n\t\tcm = allocb(sizeof(rtcp_sr_t), 0);\n\t\tcm->b_wptr += rtcp_sr_init(session, cm->b_wptr, sizeof(rtcp_sr_t));\n\t\t/* make a SDES packet */\n\t\tsdes = rtp_session_create_rtcp_sdes_packet(session, TRUE);\n\t\t/* link them */\n\t\tconcatb(concatb(cm, sdes), bye);\n\t} else if (session->stats.packet_recv > 0) {\n\t\t/* make a RR packet */\n\t\tcm = allocb(sizeof(rtcp_rr_t), 0);\n\t\tcm->b_wptr += rtcp_rr_init(session, cm->b_wptr, sizeof(rtcp_rr_t));\n\t\t/* link them */\n\t\tcm->b_cont = bye;\n\t} else cm = bye;\n\n\t/* Send compound packet. */\n\tret = rtp_session_rtcp_send(session, cm);\n\n\treturn ret;\n}\n\nOrtpLossRateEstimator *\nortp_loss_rate_estimator_new(int min_packet_count_interval, uint64_t min_time_ms_interval, RtpSession *session) {\n\tOrtpLossRateEstimator *obj = ortp_malloc(sizeof(OrtpLossRateEstimator));\n\tortp_loss_rate_estimator_init(obj, min_packet_count_interval, min_time_ms_interval, session);\n\treturn obj;\n}\n\nvoid ortp_loss_rate_estimator_init(OrtpLossRateEstimator *obj,\n                                   int min_packet_count_interval,\n                                   uint64_t min_time_ms_interval,\n                                   RtpSession *session) {\n\tmemset(obj, 0, sizeof(*obj));\n\tobj->min_packet_count_interval = min_packet_count_interval;\n\tobj->last_ext_seq = rtp_session_get_seq_number(session);\n\tobj->last_cum_loss = rtp_session_get_cum_loss(session);\n\tobj->last_packet_sent_count = session->stats.packet_sent;\n\tobj->last_dup_packet_sent_count = session->stats.packet_dup_sent;\n\tobj->min_time_ms_interval = min_time_ms_interval;\n\tobj->last_estimate_time_ms = (uint64_t)-1;\n}\n\nbool_t ortp_loss_rate_estimator_process_report_block(OrtpLossRateEstimator *obj,\n                                                     const RtpSession *session,\n                                                     const report_block_t *rb) {\n\tint32_t cum_loss = report_block_get_cum_packet_lost(rb);\n\tint32_t extseq = report_block_get_high_ext_seq(rb);\n\t// int32_t diff_unique_outgoing=(int32_t)(session->stats.packet_sent-obj->last_packet_sent_count);\n\t// int32_t\n\t// diff_total_outgoing=diff_unique_outgoing+(int32_t)(session->stats.packet_dup_sent-obj->last_dup_packet_sent_count);\n\tint32_t diff;\n\tuint64_t curtime;\n\tbool_t got_value = FALSE;\n\n\tif (obj->last_ext_seq == -1 || obj->last_estimate_time_ms == (uint64_t)-1) {\n\t\t/*first report cannot be considered, since we don't know the interval it covers*/\n\t\tobj->last_ext_seq = extseq;\n\t\tobj->last_cum_loss = cum_loss;\n\t\tobj->last_estimate_time_ms = bctbx_get_cur_time_ms();\n\t\treturn FALSE;\n\t}\n\tdiff = extseq - obj->last_ext_seq;\n\tcurtime = bctbx_get_cur_time_ms();\n\tif (diff < 0 || diff > obj->min_packet_count_interval * 100) {\n\t\tif (extseq == 0) {\n\t\t\t/*when extseq reset to 0, it probably means that rtp_session_sync was called but\n\t\t\tsince OrtplossRateEstimator is not reset, first RTCP packet received will be detected\n\t\t\tas discontinuity instead of init RTCP packet. Avoid logging in such case.*/\n\t\t\tortp_message(\n\t\t\t    \"ortp_loss_rate_estimator_process %p: Suspected RTP session restart, sequence numbering from %d to %d.\",\n\t\t\t    obj, obj->last_ext_seq, extseq);\n\t\t} else {\n\t\t\tortp_warning(\n\t\t\t    \"ortp_loss_rate_estimator_process %p: Suspected discontinuity in sequence numbering from %d to %d.\",\n\t\t\t    obj, obj->last_ext_seq, extseq);\n\t\t}\n\t\tobj->last_ext_seq = extseq;\n\t\tobj->last_cum_loss = cum_loss;\n\t\tobj->last_packet_sent_count = session->stats.packet_sent;\n\t\tobj->last_dup_packet_sent_count = session->stats.packet_dup_sent;\n\t} else if (diff > obj->min_packet_count_interval &&\n\t           curtime - obj->last_estimate_time_ms >= obj->min_time_ms_interval) {\n\t\t/*we have sufficient interval*/\n\t\tint32_t new_losses = cum_loss - obj->last_cum_loss;\n\n#if 0 /*SM: the following code try to takes into account sent duplicates - however by doing this it creates a bias in  \\\n        the loss rate computation that can sometimes result in a negative loss rate, even if there is no duplicate.    \\\n        Since the rate control doesn't use duplicates anymore, there is no good reason to take this into account.      \\\n        */\n\t\t/*if we are using duplicates, they will not be visible in 'diff' variable.\n\t\tBut since we are the emitter, we can retrieve the total count of packet we\n\t\tsent and use this value to compute the loss rate instead.*/\n\t\tobj->loss_rate = 100.f * (1.f - MAX(0, (diff_unique_outgoing - new_losses) * 1.f / diff_total_outgoing));\n#endif\n\t\tobj->loss_rate = 100.f * (float)new_losses / (float)(extseq - obj->last_ext_seq);\n\n\t\t/*update last values with current*/\n\t\tgot_value = TRUE;\n\t\tobj->last_estimate_time_ms = curtime;\n\n\t\tif (obj->loss_rate > 100.f) {\n\t\t\tobj->loss_rate = 100.f;\n\t\t\tortp_error(\"ortp_loss_rate_estimator_process %p: Loss rate MUST NOT be greater than 100%%\", obj);\n\t\t} else if (obj->loss_rate < 0) {\n\t\t\tobj->loss_rate = 0;\n\t\t\tortp_error(\"ortp_loss_rate_estimator_process %p: Loss rate MUST NOT be negative\", obj);\n\t\t}\n\t\tobj->last_ext_seq = extseq;\n\t\tobj->last_cum_loss = cum_loss;\n\t\tobj->last_packet_sent_count = session->stats.packet_sent;\n\t\tobj->last_dup_packet_sent_count = session->stats.packet_dup_sent;\n\t}\n\treturn got_value;\n}\n\nfloat ortp_loss_rate_estimator_get_value(OrtpLossRateEstimator *obj) {\n\treturn obj->loss_rate;\n}\n\nvoid ortp_loss_rate_estimator_destroy(OrtpLossRateEstimator *obj) {\n\tortp_free(obj);\n}\n"
  },
  {
    "path": "src/rtcp_fb.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"ortp/ortp.h\"\n#include \"ortp/rtcp.h\"\n#include \"ortp/rtpsession.h\"\n#include \"rtpsession_priv.h\"\n\nstatic void rtp_session_add_fb_packet_to_send(RtpSession *session, mblk_t *m) {\n\tif (session->rtcp.send_algo.fb_packets == NULL) {\n\t\tsession->rtcp.send_algo.fb_packets = m;\n\t} else {\n\t\t/*\n\t\t * CAUTION: there is no limit in the number of fb fragments that can be enqueued here.\n\t\t * When this exceeds MAX_IOV (from rtpsession_inet.c), the end will be discarded.\n\t\t * This may happen if the target upload bandwidth (rtp_session_set_target_upload_bandwidth() ) is too low\n\t\t * too allow feedback packets to be sent in real time.\n\t\t */\n\t\tconcatb(session->rtcp.send_algo.fb_packets, m);\n\t}\n}\n\nstatic bool_t is_fb_packet_to_be_sent_immediately(RtpSession *session) {\n\tuint64_t t0;\n\n\tif (rtp_session_has_fb_packets_to_send(session) == TRUE) return FALSE;\n\tt0 = bctbx_get_cur_time_ms();\n\tif (t0 > session->rtcp.send_algo.tn) return FALSE;\n\tif (session->rtcp.send_algo.allow_early == FALSE) {\n\t\tif ((session->rtcp.send_algo.tn - t0) >= session->rtcp.send_algo.T_max_fb_delay) {\n\t\t\t/* Discard message as it is considered that it will not be useful to the sender\n\t\t\t   at the time it will receive it. */\n\t\t\tfreemsg(session->rtcp.send_algo.fb_packets);\n\t\t\tsession->rtcp.send_algo.fb_packets = NULL;\n\t\t}\n\t\treturn FALSE;\n\t}\n\treturn TRUE;\n}\n\nstatic mblk_t *make_rtcp_fb_pli(RtpSession *session) {\n\tint size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t);\n\tmblk_t *h = allocb(size, 0);\n\trtcp_common_header_t *ch;\n\trtcp_fb_header_t *fbh;\n\n\t/* Fill PLI */\n\tch = (rtcp_common_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_common_header_t);\n\tfbh = (rtcp_fb_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_header_t);\n\tfbh->packet_sender_ssrc = htonl(rtp_session_get_send_ssrc(session));\n\tfbh->media_source_ssrc = htonl(rtp_session_get_recv_ssrc(session));\n\n\t/* Fill common header */\n\trtcp_common_header_init(ch, session, RTCP_PSFB, RTCP_PSFB_PLI, msgdsize(h));\n\n\treturn h;\n}\n\nstatic mblk_t *make_rtcp_fb_fir(RtpSession *session) {\n\tint size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + sizeof(rtcp_fb_fir_fci_t);\n\tmblk_t *h = allocb(size, 0);\n\trtcp_common_header_t *ch;\n\trtcp_fb_header_t *fbh;\n\trtcp_fb_fir_fci_t *fci1;\n\n\t/* Fill FIR */\n\tch = (rtcp_common_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_common_header_t);\n\tfbh = (rtcp_fb_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_header_t);\n\tfci1 = (rtcp_fb_fir_fci_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_fir_fci_t);\n\n\t/*\n\t * See https://datatracker.ietf.org/doc/rfc4585/ section 6.1\n\t * SSRC of packet sender: 32 bits\n\t * \tThe synchronization source identifier for the originator of this packet.\n\t *\n\t * SSRC of media source: 32 bits\n\t * \tThe synchronization source identifier of the media source that\n\t * \tthis piece of feedback information is related to.\n\t */\n\n\tfbh->packet_sender_ssrc = htonl(rtp_session_get_send_ssrc(session));\n\tfbh->media_source_ssrc = htonl(rtp_session_get_recv_ssrc(session));\n\n\t/*\n\t * https://www.rfc-editor.org/rfc/rfc5104.html#section-4.3.1.1\n\t * SSRC (32 bits): The SSRC value of the media sender that is\n\t * \trequested to send a decoder refresh point.\n\t */\n\n\tfci1->ssrc = htonl(rtp_session_get_recv_ssrc(session));\n\tfci1->seq_nr = session->rtcp.rtcp_fb_fir_seq_nr;\n\tfci1->pad1 = 0;\n\tfci1->pad2 = 0;\n\n\t/* Fill common header */\n\trtcp_common_header_init(ch, session, RTCP_PSFB, RTCP_PSFB_FIR, msgdsize(h));\n\n\treturn h;\n}\n\nstatic mblk_t *make_rtcp_fb_sli(RtpSession *session, uint16_t first, uint16_t number, uint8_t picture_id) {\n\tint size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + sizeof(rtcp_fb_sli_fci_t);\n\tmblk_t *h = allocb(size, 0);\n\trtcp_common_header_t *ch;\n\trtcp_fb_header_t *fbh;\n\trtcp_fb_sli_fci_t *fci;\n\n\t/* Fill SLI */\n\tch = (rtcp_common_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_common_header_t);\n\tfbh = (rtcp_fb_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_header_t);\n\tfci = (rtcp_fb_sli_fci_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_sli_fci_t);\n\tfbh->packet_sender_ssrc = htonl(rtp_session_get_send_ssrc(session));\n\tfbh->media_source_ssrc = htonl(rtp_session_get_recv_ssrc(session));\n\trtcp_fb_sli_fci_set_first(fci, first);\n\trtcp_fb_sli_fci_set_number(fci, number);\n\trtcp_fb_sli_fci_set_picture_id(fci, picture_id);\n\n\t/* Fill common header */\n\trtcp_common_header_init(ch, session, RTCP_PSFB, RTCP_PSFB_SLI, msgdsize(h));\n\n\treturn h;\n}\n\nstatic mblk_t *make_rtcp_fb_rpsi(RtpSession *session, uint8_t *bit_string, uint16_t bit_string_len) {\n\tuint16_t bit_string_len_in_bytes;\n\tint additional_bytes;\n\tint size;\n\tmblk_t *h;\n\trtcp_common_header_t *ch;\n\trtcp_fb_header_t *fbh;\n\trtcp_fb_rpsi_fci_t *fci;\n\tint i;\n\n\t/* Calculate packet size and allocate memory. */\n\tbit_string_len_in_bytes = (bit_string_len / 8) + (((bit_string_len % 8) == 0) ? 0 : 1);\n\tadditional_bytes = bit_string_len_in_bytes - 2;\n\tif (additional_bytes < 0) additional_bytes = 0;\n\tsize = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + sizeof(rtcp_fb_rpsi_fci_t) + additional_bytes;\n\th = allocb(size, 0);\n\n\t/* Fill RPSI */\n\tch = (rtcp_common_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_common_header_t);\n\tfbh = (rtcp_fb_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_header_t);\n\tfci = (rtcp_fb_rpsi_fci_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_rpsi_fci_t);\n\tfbh->packet_sender_ssrc = htonl(rtp_session_get_send_ssrc(session));\n\tfbh->media_source_ssrc = htonl(rtp_session_get_recv_ssrc(session));\n\tif (bit_string_len <= 16) {\n\t\tfci->pb = 16 - bit_string_len;\n\t\tmemset(&fci->bit_string, 0, 2);\n\t} else {\n\t\tfci->pb = (bit_string_len - 16) % 32;\n\t\tmemset(&fci->bit_string, 0, bit_string_len_in_bytes);\n\t}\n\tfci->payload_type = rtp_session_get_recv_payload_type(session) & 0x7F;\n\tmemcpy(&fci->bit_string, bit_string, bit_string_len / 8);\n\tfor (i = 0; i < (bit_string_len % 8); i++) {\n\t\tfci->bit_string[bit_string_len_in_bytes - 1] |= (bit_string[bit_string_len_in_bytes - 1] & (1 << (7 - i)));\n\t}\n\n\t/* Fill common header */\n\trtcp_common_header_init(ch, session, RTCP_PSFB, RTCP_PSFB_RPSI, msgdsize(h));\n\n\treturn h;\n}\n\nstatic mblk_t *make_rtcp_fb_generic_nack(RtpSession *session, uint16_t pid, uint16_t blp) {\n\tint size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + sizeof(rtcp_fb_generic_nack_fci_t);\n\tmblk_t *h = allocb(size, 0);\n\trtcp_common_header_t *ch;\n\trtcp_fb_header_t *fbh;\n\trtcp_fb_generic_nack_fci_t *fci;\n\n\tch = (rtcp_common_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_common_header_t);\n\tfbh = (rtcp_fb_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_header_t);\n\tfci = (rtcp_fb_generic_nack_fci_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_generic_nack_fci_t);\n\tfbh->packet_sender_ssrc = htonl(rtp_session_get_send_ssrc(session));\n\tfbh->media_source_ssrc = htonl(0);\n\trtcp_fb_generic_nack_fci_set_pid(fci, pid);\n\trtcp_fb_generic_nack_fci_set_blp(fci, blp);\n\n\t/* Fill common header */\n\trtcp_common_header_init(ch, session, RTCP_RTPFB, RTCP_RTPFB_NACK, msgdsize(h));\n\n\treturn h;\n}\n\nstatic mblk_t *make_rtcp_fb_tmmbr(RtpSession *session, uint64_t mxtbr, uint16_t measured_overhead) {\n\tint size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + sizeof(rtcp_fb_tmmbr_fci_t);\n\tmblk_t *h = allocb(size, 0);\n\trtcp_common_header_t *ch;\n\trtcp_fb_header_t *fbh;\n\trtcp_fb_tmmbr_fci_t *fci;\n\tuint8_t mxtbr_exp = 0;\n\tuint32_t mxtbr_mantissa = 0;\n\n\t/* Compute mxtbr exp and mantissa */\n\twhile (mxtbr >= (1 << 17)) {\n\t\tmxtbr >>= 1;\n\t\tmxtbr_exp++;\n\t}\n\tmxtbr_mantissa = mxtbr & 0x0001FFFF;\n\n\t/* Fill TMMBR */\n\tch = (rtcp_common_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_common_header_t);\n\tfbh = (rtcp_fb_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_header_t);\n\tfci = (rtcp_fb_tmmbr_fci_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_tmmbr_fci_t);\n\tfbh->packet_sender_ssrc = htonl(rtp_session_get_send_ssrc(session));\n\tfbh->media_source_ssrc = htonl(0);\n\tfci->ssrc = htonl(rtp_session_get_recv_ssrc(session));\n\trtcp_fb_tmmbr_fci_set_mxtbr_exp(fci, mxtbr_exp);\n\trtcp_fb_tmmbr_fci_set_mxtbr_mantissa(fci, mxtbr_mantissa);\n\trtcp_fb_tmmbr_fci_set_measured_overhead(fci, measured_overhead);\n\n\t/* Fill common header */\n\trtcp_common_header_init(ch, session, RTCP_RTPFB, RTCP_RTPFB_TMMBR, msgdsize(h));\n\n\t/* Store packet to be able to retransmit. */\n\tif (session->rtcp.tmmbr_info.sent) freemsg(session->rtcp.tmmbr_info.sent);\n\tsession->rtcp.tmmbr_info.sent = copymsg(h);\n\n\treturn h;\n}\n\nstatic mblk_t *make_rtcp_fb_tmmbn(RtpSession *session, uint32_t ssrc) {\n\tint size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + sizeof(rtcp_fb_tmmbr_fci_t);\n\tmblk_t *h = allocb(size, 0);\n\trtcp_common_header_t *ch;\n\trtcp_fb_header_t *fbh;\n\trtcp_fb_tmmbr_fci_t *fci;\n\n\tif (!session->rtcp.tmmbr_info.received) return NULL;\n\n\t/* Fill TMMBN */\n\tch = (rtcp_common_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_common_header_t);\n\tfbh = (rtcp_fb_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_header_t);\n\tfci = (rtcp_fb_tmmbr_fci_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_tmmbr_fci_t);\n\tfbh->packet_sender_ssrc = htonl(rtp_session_get_send_ssrc(session));\n\tfbh->media_source_ssrc = htonl(0);\n\tmemcpy(fci, rtcp_RTPFB_tmmbr_get_fci(session->rtcp.tmmbr_info.received), sizeof(rtcp_fb_tmmbr_fci_t));\n\tfci->ssrc = htonl(ssrc);\n\n\t/* Fill common header */\n\trtcp_common_header_init(ch, session, RTCP_RTPFB, RTCP_RTPFB_TMMBN, msgdsize(h));\n\n\treturn h;\n}\n\n// See https://datatracker.ietf.org/doc/html/draft-alvestrand-rmcat-remb-03\nstatic mblk_t *make_rtcp_fb_goog_remb(RtpSession *session, uint64_t mxtbr) {\n\tconst int size =\n\t    sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + sizeof(rtcp_fb_goog_remb_fci_t) + sizeof(uint32_t);\n\tmblk_t *h = allocb(size, 0);\n\tuint8_t mxtbr_exp = 0;\n\tuint32_t mxtbr_mantissa = 0;\n\n\t/* Compute mxtbr exp and mantissa */\n\twhile (mxtbr >= (1 << 18)) {\n\t\tmxtbr >>= 1;\n\t\tmxtbr_exp++;\n\t}\n\tmxtbr_mantissa = mxtbr & 0x0003FFFF;\n\n\trtcp_common_header_t *ch = (rtcp_common_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_common_header_t);\n\n\t/* Fill RTCP FB header */\n\trtcp_fb_header_t *fbh = (rtcp_fb_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_header_t);\n\n\tfbh->packet_sender_ssrc = htonl(rtp_session_get_send_ssrc(session));\n\tfbh->media_source_ssrc = htonl(0);\n\n\t/* Fill REMB */\n\trtcp_fb_goog_remb_fci_t *fci = (rtcp_fb_goog_remb_fci_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_goog_remb_fci_t);\n\n\tfci->identifier = htonl(0x52454D42); // REMB in ascii\n\n\trtcp_fb_goog_remb_fci_set_num_ssrc(fci, 1);\n\trtcp_fb_goog_remb_fci_set_mxtbr_exp(fci, mxtbr_exp);\n\trtcp_fb_goog_remb_fci_set_mxtbr_mantissa(fci, mxtbr_mantissa);\n\n\t/* Fill SSRCs */\n\tuint32_t *ssrc = (uint32_t *)h->b_wptr;\n\th->b_wptr += sizeof(uint32_t);\n\n\t*ssrc = htonl(session->rcv.ssrc);\n\n\t/* Fill common header */\n\trtcp_common_header_init(ch, session, RTCP_PSFB, RTCP_PSFB_AFB, msgdsize(h));\n\n\t/* Store packet to be able to retransmit. */\n\tif (session->rtcp.goog_remb_info.sent) freemsg(session->rtcp.goog_remb_info.sent);\n\tsession->rtcp.goog_remb_info.sent = copymsg(h);\n\n\treturn h;\n}\n\nbool_t rtp_session_rtcp_psfb_scheduled(RtpSession *session, rtcp_psfb_type_t type) {\n\tmblk_t *m = session->rtcp.send_algo.fb_packets;\n\twhile (m != NULL) {\n\t\tif ((rtcp_is_PSFB_internal(m) == TRUE) && (rtcp_PSFB_get_type(m) == type)) {\n\t\t\treturn TRUE;\n\t\t}\n\t\tm = m->b_cont;\n\t}\n\treturn FALSE;\n}\n\nbool_t rtp_session_rtcp_rtpfb_scheduled(RtpSession *session, rtcp_rtpfb_type_t type) {\n\tmblk_t *m = session->rtcp.send_algo.fb_packets;\n\twhile (m != NULL) {\n\t\tif ((rtcp_is_RTPFB_internal(m) == TRUE) && (rtcp_RTPFB_get_type(m) == type)) {\n\t\t\treturn TRUE;\n\t\t}\n\t\tm = m->b_cont;\n\t}\n\treturn FALSE;\n}\n\nvoid rtp_session_send_rtcp_fb_generic_nack(RtpSession *session, uint16_t pid, uint16_t blp) {\n\tmblk_t *m;\n\tif ((rtp_session_avpf_enabled(session) == TRUE) &&\n\t    (rtp_session_avpf_feature_enabled(session, ORTP_AVPF_FEATURE_GENERIC_NACK) == TRUE)) {\n\t\tm = make_rtcp_fb_generic_nack(session, pid, blp);\n\t\trtp_session_add_fb_packet_to_send(session, m);\n\t\trtp_session_send_fb_rtcp_packet_and_reschedule(session);\n\t}\n}\n\nvoid rtp_session_send_rtcp_fb_pli(RtpSession *session) {\n\tmblk_t *m;\n\tif ((rtp_session_avpf_enabled(session) == TRUE) &&\n\t    (rtp_session_avpf_payload_type_feature_enabled(session, PAYLOAD_TYPE_AVPF_PLI) == TRUE)) {\n\t\tbool_t can_send_immediately = FALSE;\n\t\tif (rtp_session_rtcp_psfb_scheduled(session, RTCP_PSFB_PLI) != TRUE) {\n\t\t\tm = make_rtcp_fb_pli(session);\n\t\t\tcan_send_immediately = is_fb_packet_to_be_sent_immediately(session);\n\t\t\trtp_session_add_fb_packet_to_send(session, m);\n\t\t}\n\t\tif (can_send_immediately) {\n\t\t\trtp_session_send_fb_rtcp_packet_and_reschedule(session);\n\t\t}\n\t}\n}\n\nvoid rtp_session_send_rtcp_fb_fir(RtpSession *session) {\n\tmblk_t *m;\n\tif ((rtp_session_avpf_enabled(session) == TRUE) &&\n\t    (rtp_session_avpf_payload_type_feature_enabled(session, PAYLOAD_TYPE_AVPF_FIR) == TRUE)) {\n\t\tbool_t can_send_immediately = FALSE;\n\t\tif (rtp_session_rtcp_psfb_scheduled(session, RTCP_PSFB_FIR) != TRUE) {\n\t\t\tm = make_rtcp_fb_fir(session);\n\t\t\tcan_send_immediately = is_fb_packet_to_be_sent_immediately(session);\n\t\t\trtp_session_add_fb_packet_to_send(session, m);\n\t\t}\n\t\tif (can_send_immediately) {\n\t\t\trtp_session_send_fb_rtcp_packet_and_reschedule(session);\n\t\t}\n\t}\n}\n\nvoid rtp_session_send_rtcp_fb_sli(RtpSession *session, uint16_t first, uint16_t number, uint8_t picture_id) {\n\tmblk_t *m;\n\tif (rtp_session_avpf_enabled(session) == TRUE) {\n\t\t/* Only send SLI if SLI and RPSI features have been enabled. SLI without RPSI is not really useful. */\n\t\tif ((rtp_session_avpf_payload_type_feature_enabled(session, PAYLOAD_TYPE_AVPF_SLI) == TRUE) &&\n\t\t    (rtp_session_avpf_payload_type_feature_enabled(session, PAYLOAD_TYPE_AVPF_RPSI) == TRUE)) {\n\t\t\t/* we check first if the packet can be sent immediately. is_fb_packet_to_be_sent_immediately() will return\n\t\t\t * FALSE if there are queued feedback packets, which we are going to do in\n\t\t\t * rtp_session_add_fb_packet_to_send() just after.\n\t\t\t */\n\t\t\tbool_t can_send_immediately = is_fb_packet_to_be_sent_immediately(session);\n\t\t\tm = make_rtcp_fb_sli(session, first, number, picture_id);\n\t\t\trtp_session_add_fb_packet_to_send(session, m);\n\t\t\tif (can_send_immediately) {\n\t\t\t\trtp_session_send_fb_rtcp_packet_and_reschedule(session);\n\t\t\t}\n\t\t} else {\n\t\t\t// Try to fallback to sending a PLI if the SLI feature has not been enabled.\n\t\t\trtp_session_send_rtcp_fb_pli(session);\n\t\t}\n\t}\n}\n\nvoid rtp_session_send_rtcp_fb_rpsi(RtpSession *session, uint8_t *bit_string, uint16_t bit_string_len) {\n\tmblk_t *m;\n\tif ((rtp_session_avpf_enabled(session) == TRUE) &&\n\t    (rtp_session_avpf_payload_type_feature_enabled(session, PAYLOAD_TYPE_AVPF_RPSI) == TRUE)) {\n\t\tbool_t can_send_immediately;\n\t\tm = make_rtcp_fb_rpsi(session, bit_string, bit_string_len);\n\t\tcan_send_immediately = is_fb_packet_to_be_sent_immediately(session);\n\t\trtp_session_add_fb_packet_to_send(session, m);\n\t\tif (can_send_immediately) {\n\t\t\trtp_session_send_fb_rtcp_packet_and_reschedule(session);\n\t\t}\n\t}\n}\n\nvoid rtp_session_send_rtcp_fb_tmmbr(RtpSession *session, uint64_t mxtbr) {\n\tmblk_t *m;\n\tif ((rtp_session_avpf_enabled(session) == TRUE) &&\n\t    (rtp_session_avpf_feature_enabled(session, ORTP_AVPF_FEATURE_TMMBR) == TRUE)) {\n\t\tif ((rtp_session_rtcp_rtpfb_scheduled(session, RTCP_RTPFB_TMMBR) != TRUE) &&\n\t\t    (rtp_session_get_recv_ssrc(session) != 0)) {\n\t\t\tuint16_t overhead = (session->rtp.gs.sockfamily == AF_INET6) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;\n\t\t\tm = make_rtcp_fb_tmmbr(session, mxtbr, overhead);\n\t\t\trtp_session_add_fb_packet_to_send(session, m);\n\t\t\tsession->rtcp.send_algo.tmmbr_scheduled = TRUE;\n\t\t}\n\t\trtp_session_send_fb_rtcp_packet_and_reschedule(session);\n\t}\n}\n\nvoid rtp_session_send_rtcp_fb_tmmbn(RtpSession *session, uint32_t ssrc) {\n\tmblk_t *m;\n\tif ((rtp_session_avpf_enabled(session) == TRUE) &&\n\t    (rtp_session_avpf_feature_enabled(session, ORTP_AVPF_FEATURE_TMMBR) == TRUE)) {\n\t\tm = make_rtcp_fb_tmmbn(session, ssrc);\n\t\tif (m) {\n\t\t\trtp_session_add_fb_packet_to_send(session, m);\n\t\t\tsession->rtcp.send_algo.tmmbn_scheduled = TRUE;\n\t\t}\n\t\trtp_session_send_fb_rtcp_packet_and_reschedule(session);\n\t}\n}\n\nvoid rtp_session_send_rtcp_fb_goog_remb(RtpSession *session, uint64_t mxtbr) {\n\tif ((rtp_session_avpf_enabled(session) == TRUE) &&\n\t    (rtp_session_avpf_feature_enabled(session, ORTP_AVPF_FEATURE_GOOG_REMB) == TRUE)) {\n\t\tbool_t can_send_immediately = FALSE;\n\t\tif (rtp_session_rtcp_psfb_scheduled(session, RTCP_PSFB_AFB) != TRUE) {\n\t\t\tmblk_t *m = make_rtcp_fb_goog_remb(session, mxtbr);\n\t\t\tcan_send_immediately = is_fb_packet_to_be_sent_immediately(session);\n\t\t\trtp_session_add_fb_packet_to_send(session, m);\n\t\t\tsession->rtcp.send_algo.goog_remb_scheduled = TRUE;\n\t\t}\n\t\tif (can_send_immediately) rtp_session_send_fb_rtcp_packet_and_reschedule(session);\n\t}\n}\n\nbool_t rtp_session_avpf_enabled(RtpSession *session) {\n\tPayloadType *pt = rtp_profile_get_payload(session->snd.profile, session->snd.pt);\n\tif (!pt) {\n\t\tortp_warning(\"rtp_session_avpf_enabled(): payload type not set, unreliable result returned.\");\n\t}\n\treturn pt && (payload_type_get_flags(pt) & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);\n}\n\nbool_t rtp_session_avpf_payload_type_feature_enabled(RtpSession *session, unsigned char feature) {\n\tPayloadType *pt = rtp_profile_get_payload(session->snd.profile, session->snd.pt);\n\tPayloadTypeAvpfParams params;\n\tif (!pt) return FALSE;\n\tparams = payload_type_get_avpf_params(pt);\n\tif (params.features & feature) return TRUE;\n\treturn FALSE;\n}\n\nbool_t rtp_session_avpf_feature_enabled(RtpSession *session, unsigned char feature) {\n\tif (session->avpf_features & feature) return TRUE;\n\treturn FALSE;\n}\n\nvoid rtp_session_enable_avpf_feature(RtpSession *session, unsigned char feature, bool_t enable) {\n\tif (enable) {\n\t\tsession->avpf_features |= feature;\n\t} else {\n\t\tsession->avpf_features &= ~feature;\n\t}\n}\n\nuint16_t rtp_session_get_avpf_rr_interval(RtpSession *session) {\n\tPayloadType *pt = rtp_profile_get_payload(session->rcv.profile, session->rcv.pt);\n\tPayloadTypeAvpfParams params;\n\tif (!pt) return RTCP_DEFAULT_REPORT_INTERVAL;\n\tparams = payload_type_get_avpf_params(pt);\n\treturn (uint16_t)params.trr_interval;\n}\n\nbool_t rtp_session_has_fb_packets_to_send(RtpSession *session) {\n\treturn (session->rtcp.send_algo.fb_packets == NULL) ? FALSE : TRUE;\n}\n"
  },
  {
    "path": "src/rtcp_xr.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <bctoolbox/defs.h>\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include <math.h>\n\n#include \"ortp/ortp.h\"\n#include \"ortp/rtcp.h\"\n#include \"ortp/rtpsession.h\"\n#include \"rtpsession_priv.h\"\n#include \"utils.h\"\n\nstatic uint8_t calc_rate(double d1, double d2) {\n\tdouble rate = (d1 / d2) * 256;\n\tuint32_t int_rate = (uint32_t)rate;\n\tif (int_rate > 255) int_rate = 255;\n\treturn (uint8_t)int_rate;\n}\n\nstatic int rtcp_xr_header_init(uint8_t *buf, RtpSession *session, int bytes_len) {\n\trtcp_xr_header_t *header = (rtcp_xr_header_t *)buf;\n\trtcp_common_header_init(&header->ch, session, RTCP_XR, 0, bytes_len);\n\theader->ssrc = htonl(session->snd.ssrc);\n\treturn sizeof(rtcp_xr_header_t);\n}\n\nstatic int rtcp_xr_rcvr_rtt_init(uint8_t *buf, BCTBX_UNUSED(RtpSession *session)) {\n\tstruct timeval tv;\n\tuint64_t ntp;\n\trtcp_xr_rcvr_rtt_report_block_t *block = (rtcp_xr_rcvr_rtt_report_block_t *)buf;\n\n\tblock->bh.bt = RTCP_XR_RCVR_RTT;\n\tblock->bh.flags = 0; // Reserved bits\n\tblock->bh.length = htons(2);\n\tbctbx_gettimeofday(&tv, NULL);\n\tntp = ortp_timeval_to_ntp(&tv);\n\tblock->ntp_timestamp_msw = htonl(ntp >> 32);\n\tblock->ntp_timestamp_lsw = htonl(ntp & 0xFFFFFFFF);\n\treturn sizeof(rtcp_xr_rcvr_rtt_report_block_t);\n}\n\nstatic int rtcp_xr_dlrr_init(uint8_t *buf, RtpSession *session) {\n\tuint32_t dlrr = 0;\n\trtcp_xr_dlrr_report_block_t *block = (rtcp_xr_dlrr_report_block_t *)buf;\n\n\tblock->bh.bt = RTCP_XR_DLRR;\n\tblock->bh.flags = 0; // Reserved bits\n\tblock->bh.length = htons(3);\n\tblock->content[0].ssrc = htonl(rtp_session_get_recv_ssrc(session));\n\tblock->content[0].lrr = htonl(session->rtcp_xr_stats.last_rcvr_rtt_ts);\n\tif (session->rtcp_xr_stats.last_rcvr_rtt_time.tv_sec != 0) {\n\t\tstruct timeval now;\n\t\tdouble delay;\n\t\tbctbx_gettimeofday(&now, NULL);\n\t\tdelay = ((now.tv_sec - session->rtcp_xr_stats.last_rcvr_rtt_time.tv_sec) +\n\t\t         ((now.tv_usec - session->rtcp_xr_stats.last_rcvr_rtt_time.tv_usec) * 1e-6)) *\n\t\t        65536;\n\t\tdlrr = (uint32_t)delay;\n\t}\n\tblock->content[0].dlrr = htonl(dlrr);\n\treturn sizeof(rtcp_xr_dlrr_report_block_t);\n}\n\nstatic int rtcp_xr_stat_summary_init(uint8_t *buf, RtpSession *session) {\n\trtcp_xr_stat_summary_report_block_t *block = (rtcp_xr_stat_summary_report_block_t *)buf;\n\tuint16_t last_rcv_seq = session->rtp.hwrcv_extseq & 0xFFFF;\n\tuint8_t flags = session->rtcp.xr_conf.stat_summary_flags;\n\tuint32_t expected_packets;\n\tuint32_t lost_packets = 0;\n\tuint32_t dup_packets = session->rtcp_xr_stats.dup_since_last_stat_summary;\n\n\t/* Compute lost and duplicate packets statistics */\n\tif (flags & OrtpRtcpXrStatSummaryLoss) {\n\t\tuint32_t no_duplicate_received = session->rtcp_xr_stats.rcv_since_last_stat_summary - dup_packets;\n\t\texpected_packets = last_rcv_seq - session->rtcp_xr_stats.rcv_seq_at_last_stat_summary;\n\t\tlost_packets = (expected_packets > session->rtcp_xr_stats.rcv_since_last_stat_summary)\n\t\t                   ? (expected_packets - no_duplicate_received)\n\t\t                   : 0;\n\t}\n\n\tblock->bh.bt = RTCP_XR_STAT_SUMMARY;\n\tblock->bh.flags = flags;\n\tblock->bh.length = htons(9);\n\tblock->ssrc = htonl(rtp_session_get_recv_ssrc(session));\n\tblock->begin_seq = htons(session->rtcp_xr_stats.rcv_seq_at_last_stat_summary + 1);\n\tblock->end_seq = htons(last_rcv_seq + 1);\n\tblock->lost_packets = htonl(lost_packets);\n\tblock->dup_packets = htonl(dup_packets);\n\tif ((flags & OrtpRtcpXrStatSummaryJitt) && (session->rtcp_xr_stats.rcv_since_last_stat_summary > 0)) {\n\t\tblock->min_jitter = htonl(session->rtcp_xr_stats.min_jitter_since_last_stat_summary);\n\t\tblock->max_jitter = htonl(session->rtcp_xr_stats.max_jitter_since_last_stat_summary);\n\t\tblock->mean_jitter = htonl((session->rtcp_xr_stats.rcv_since_last_stat_summary > 1)\n\t\t                               ? (uint32_t)session->rtcp_xr_stats.newm_jitter_since_last_stat_summary\n\t\t                               : 0);\n\t\tblock->dev_jitter = htonl((session->rtcp_xr_stats.rcv_since_last_stat_summary > 2)\n\t\t                              ? (uint32_t)sqrt(session->rtcp_xr_stats.news_jitter_since_last_stat_summary /\n\t\t                                               (session->rtcp_xr_stats.rcv_since_last_stat_summary - 2))\n\t\t                              : 0);\n\t} else {\n\t\tblock->min_jitter = htonl(0);\n\t\tblock->max_jitter = htonl(0);\n\t\tblock->mean_jitter = htonl(0);\n\t\tblock->dev_jitter = htonl(0);\n\t}\n\tif ((flags & (OrtpRtcpXrStatSummaryTTL | OrtpRtcpXrStatSummaryHL)) &&\n\t    (session->rtcp_xr_stats.rcv_since_last_stat_summary > 0)) {\n\t\tblock->min_ttl_or_hl = session->rtcp_xr_stats.min_ttl_or_hl_since_last_stat_summary;\n\t\tblock->max_ttl_or_hl = session->rtcp_xr_stats.max_ttl_or_hl_since_last_stat_summary;\n\t\tblock->mean_ttl_or_hl = (session->rtcp_xr_stats.rcv_since_last_stat_summary > 0)\n\t\t                            ? (uint8_t)session->rtcp_xr_stats.newm_ttl_or_hl_since_last_stat_summary\n\t\t                            : 0;\n\t\tblock->dev_ttl_or_hl = (session->rtcp_xr_stats.rcv_since_last_stat_summary > 1)\n\t\t                           ? (uint8_t)sqrt(session->rtcp_xr_stats.news_ttl_or_hl_since_last_stat_summary /\n\t\t                                           (session->rtcp_xr_stats.rcv_since_last_stat_summary - 1))\n\t\t                           : 0;\n\t} else {\n\t\tblock->min_ttl_or_hl = 0;\n\t\tblock->max_ttl_or_hl = 0;\n\t\tblock->mean_ttl_or_hl = 0;\n\t\tblock->dev_ttl_or_hl = 0;\n\t}\n\n\tsession->rtcp_xr_stats.rcv_seq_at_last_stat_summary = last_rcv_seq;\n\tsession->rtcp_xr_stats.rcv_since_last_stat_summary = 0;\n\tsession->rtcp_xr_stats.dup_since_last_stat_summary = 0;\n\n\treturn sizeof(rtcp_xr_stat_summary_report_block_t);\n}\n\nstatic int rtcp_xr_voip_metrics_init(uint8_t *buf, RtpSession *session) {\n\tJBParameters jbparams;\n\tuint32_t expected_packets;\n\tuint32_t lost_packets;\n\trtcp_xr_voip_metrics_report_block_t *block = (rtcp_xr_voip_metrics_report_block_t *)buf;\n\tfloat rtt = rtp_session_get_round_trip_propagation(session);\n\tuint16_t int_rtt = (uint16_t)((rtt >= 0) ? (rtt * 1000) : 0);\n\tfloat qi = -1;\n\tfloat lq_qi = -1;\n\n\trtp_session_get_jitter_buffer_params(session, &jbparams);\n\tif (session->rtcp.xr_media_callbacks.average_qi != NULL) {\n\t\tqi = session->rtcp.xr_media_callbacks.average_qi(session->rtcp.xr_media_callbacks.userdata);\n\t}\n\tif (session->rtcp.xr_media_callbacks.average_lq_qi != NULL) {\n\t\tlq_qi = session->rtcp.xr_media_callbacks.average_lq_qi(session->rtcp.xr_media_callbacks.userdata);\n\t}\n\n\tblock->bh.bt = RTCP_XR_VOIP_METRICS;\n\tblock->bh.flags = 0; // Reserved bits\n\tblock->bh.length = htons(8);\n\tblock->ssrc = htonl(rtp_session_get_recv_ssrc(session));\n\tblock->gmin = RTCP_XR_GMIN;\n\tblock->reserved2 = 0;\n\n\t// Fill RX config\n\tblock->rx_config = 0;\n\tif (jbparams.adaptive) {\n\t\tblock->rx_config |= RTCP_XR_VOIP_METRICS_CONFIG_JBA_ADA;\n\t} else {\n\t\tblock->rx_config |= RTCP_XR_VOIP_METRICS_CONFIG_JBA_NON;\n\t}\n\tif (session->rtcp.xr_media_callbacks.plc != NULL) {\n\t\tswitch (session->rtcp.xr_media_callbacks.plc(session->rtcp.xr_media_callbacks.userdata)) {\n\t\t\tdefault:\n\t\t\tcase OrtpRtcpXrNoPlc:\n\t\t\t\tblock->rx_config |= RTCP_XR_VOIP_METRICS_CONFIG_PLC_UNS;\n\t\t\t\tbreak;\n\t\t\tcase OrtpRtcpXrSilencePlc:\n\t\t\t\tblock->rx_config |= RTCP_XR_VOIP_METRICS_CONFIG_PLC_DIS;\n\t\t\t\tbreak;\n\t\t\tcase OrtpRtcpXrEnhancedPlc:\n\t\t\t\tblock->rx_config |= RTCP_XR_VOIP_METRICS_CONFIG_PLC_ENH;\n\t\t\t\tbreak;\n\t\t}\n\t} else {\n\t\tblock->rx_config |= RTCP_XR_VOIP_METRICS_CONFIG_PLC_UNS;\n\t}\n\n\t// Fill JB fields\n\tblock->jb_nominal = htons((uint16_t)jbparams.nom_size);\n\tif (jbparams.adaptive) {\n\t\tblock->jb_maximum = htons((session->rtp.jittctl.adapt_jitt_comp_ts * 1000) / session->rtp.jittctl.clock_rate);\n\t} else {\n\t\tblock->jb_maximum = block->jb_nominal;\n\t}\n\tblock->jb_abs_max = htons(65535);\n\n\tif (session->rtcp_xr_stats.rcv_count > 0) {\n\t\texpected_packets = session->rtcp_xr_stats.last_rcv_seq - session->rtcp_xr_stats.first_rcv_seq + 1;\n\t\tlost_packets = expected_packets - session->rtcp_xr_stats.rcv_count;\n\t\tblock->loss_rate = calc_rate((double)lost_packets, (double)expected_packets);\n\t\tblock->discard_rate = calc_rate((double)session->rtcp_xr_stats.discarded_count, (double)expected_packets);\n\t\t// TODO: fill burst_density, gap_density, burst_duration, gap_duration\n\t\tblock->burst_density = 0;\n\t\tblock->gap_density = 0;\n\t\tblock->burst_duration = htons(0);\n\t\tblock->gap_duration = htons(0);\n\t\tblock->round_trip_delay = htons(int_rtt);\n\t\t// TODO: fill end_system_delay\n\t\tblock->end_system_delay = htons(0);\n\t\tif (session->rtcp.xr_media_callbacks.signal_level != NULL) {\n\t\t\tblock->signal_level =\n\t\t\t    session->rtcp.xr_media_callbacks.signal_level(session->rtcp.xr_media_callbacks.userdata);\n\t\t} else {\n\t\t\tblock->signal_level = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\t}\n\t\tif (session->rtcp.xr_media_callbacks.noise_level != NULL) {\n\t\t\tblock->noise_level =\n\t\t\t    session->rtcp.xr_media_callbacks.noise_level(session->rtcp.xr_media_callbacks.userdata);\n\t\t} else {\n\t\t\tblock->noise_level = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\t}\n\t\tblock->rerl = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\tif (qi < 0) {\n\t\t\tblock->r_factor = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\t} else {\n\t\t\tblock->r_factor = (uint8_t)(qi * 20);\n\t\t}\n\t\tblock->ext_r_factor = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\tif (lq_qi < 0) {\n\t\t\tblock->mos_lq = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\t} else {\n\t\t\tblock->mos_lq = (uint8_t)(lq_qi * 10);\n\t\t\tif (block->mos_lq < 10) block->mos_lq = 10;\n\t\t}\n\t\tif (qi < 0) {\n\t\t\tblock->mos_cq = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\t} else {\n\t\t\tblock->mos_cq = (uint8_t)(qi * 10);\n\t\t\tif (block->mos_cq < 10) block->mos_cq = 10;\n\t\t}\n\t} else {\n\t\tblock->loss_rate = 0;\n\t\tblock->discard_rate = 0;\n\t\tblock->burst_density = 0;\n\t\tblock->gap_density = 0;\n\t\tblock->burst_duration = htons(0);\n\t\tblock->gap_duration = htons(0);\n\t\tblock->round_trip_delay = htons(0);\n\t\tblock->end_system_delay = htons(0);\n\t\tblock->signal_level = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\tblock->noise_level = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\tblock->rerl = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\tblock->r_factor = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\tblock->ext_r_factor = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\tblock->mos_lq = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t\tblock->mos_cq = ORTP_RTCP_XR_UNAVAILABLE_PARAMETER;\n\t}\n\treturn sizeof(rtcp_xr_voip_metrics_report_block_t);\n}\n\nmblk_t *make_xr_rcvr_rtt(RtpSession *session) {\n\tint size = sizeof(rtcp_xr_header_t) + sizeof(rtcp_xr_rcvr_rtt_report_block_t);\n\tmblk_t *h = allocb(size, 0);\n\th->b_wptr += rtcp_xr_header_init(h->b_wptr, session, size);\n\th->b_wptr += rtcp_xr_rcvr_rtt_init(h->b_wptr, session);\n\treturn h;\n}\n\nmblk_t *make_xr_dlrr(RtpSession *session) {\n\tint size = sizeof(rtcp_xr_header_t) + sizeof(rtcp_xr_dlrr_report_block_t);\n\tmblk_t *h = allocb(size, 0);\n\th->b_wptr += rtcp_xr_header_init(h->b_wptr, session, size);\n\th->b_wptr += rtcp_xr_dlrr_init(h->b_wptr, session);\n\treturn h;\n}\n\nmblk_t *make_xr_stat_summary(RtpSession *session) {\n\tint size = sizeof(rtcp_xr_header_t) + sizeof(rtcp_xr_stat_summary_report_block_t);\n\tmblk_t *h = allocb(size, 0);\n\th->b_wptr += rtcp_xr_header_init(h->b_wptr, session, size);\n\th->b_wptr += rtcp_xr_stat_summary_init(h->b_wptr, session);\n\treturn h;\n}\n\nmblk_t *make_xr_voip_metrics(RtpSession *session) {\n\tint size = sizeof(rtcp_xr_header_t) + sizeof(rtcp_xr_voip_metrics_report_block_t);\n\tmblk_t *h = allocb(size, 0);\n\th->b_wptr += rtcp_xr_header_init(h->b_wptr, session, size);\n\th->b_wptr += rtcp_xr_voip_metrics_init(h->b_wptr, session);\n\treturn h;\n}\n\nvoid rtp_session_configure_rtcp_xr(RtpSession *session, const OrtpRtcpXrConfiguration *config) {\n\tif (config != NULL) {\n\t\tsession->rtcp.xr_conf = *config;\n\t}\n}\n\nvoid rtp_session_set_rtcp_xr_media_callbacks(RtpSession *session, const OrtpRtcpXrMediaCallbacks *cbs) {\n\tif (cbs != NULL) {\n\t\tmemcpy(&session->rtcp.xr_media_callbacks, cbs, sizeof(session->rtcp.xr_media_callbacks));\n\t} else {\n\t\tmemset(&session->rtcp.xr_media_callbacks, 0, sizeof(session->rtcp.xr_media_callbacks));\n\t}\n}\n"
  },
  {
    "path": "src/rtcpparse.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include \"ortp/ortp.h\"\n#include \"ortp/str_utils.h\"\n#include \"utils.h\"\n\n/*in case of compound packet, set read pointer of m to the beginning of the next RTCP\npacket */\nbool_t _rtcp_next_packet(mblk_t *m) {\n\tsize_t nextlen = rtcp_get_size(m);\n\tif ((nextlen > 0) && (m->b_rptr + nextlen < m->b_wptr)) {\n\t\tm->b_rptr += nextlen;\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n}\n\nconst mblk_t *rtcp_parser_context_init(RtcpParserContext *context, const mblk_t *compound_packet) {\n\tcontext->current_packet =\n\t    dupmsg((mblk_t *)compound_packet); /* const qualifier discarded - the returned packet is const anyway.*/\n\tif (context->current_packet->b_cont) msgpullup(context->current_packet, (size_t)-1);\n\tcontext->start = context->current_packet->b_rptr;\n\treturn context->current_packet;\n}\n\nconst mblk_t *rtcp_parser_context_next_packet(RtcpParserContext *context) {\n\tif (_rtcp_next_packet(context->current_packet)) return context->current_packet;\n\treturn NULL;\n}\n\nconst mblk_t *rtcp_parser_context_start(RtcpParserContext *context) {\n\tcontext->current_packet->b_rptr = context->start;\n\treturn context->current_packet;\n}\n\nvoid rtcp_parser_context_uninit(RtcpParserContext *context) {\n\tif (context->current_packet) freemsg(context->current_packet);\n}\n\nsize_t rtcp_get_size(const mblk_t *m) {\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\tsize_t ret, totalsize;\n\tif (ch == NULL) return 0;\n\tret = (1 + rtcp_common_header_get_length(ch)) * 4;\n\ttotalsize = m->b_wptr - m->b_rptr;\n\tif (ret > totalsize) {\n\t\tortp_warning(\"RTCP packet indicates size [%i] which goes behond end of packet [%i], truncating.\", (int)ret,\n\t\t             (int)totalsize);\n\t\tret = totalsize;\n\t}\n\treturn ret;\n}\n\n/* deprecated function */\nbool_t rtcp_next_packet(mblk_t *m) {\n\treturn _rtcp_next_packet(m);\n}\n\n/* deprecated function */\nvoid rtcp_rewind(mblk_t *m) {\n\tm->b_rptr = m->b_datap->db_base;\n}\n\n/* get common header; this function will also check the sanity of the packet*/\nconst rtcp_common_header_t *rtcp_get_common_header(const mblk_t *m) {\n\tsize_t size = msgdsize(m);\n\trtcp_common_header_t *ch;\n\tif (size < sizeof(rtcp_common_header_t)) {\n\t\tortp_warning(\"Bad RTCP packet, too short [%i byte] on block [%p]\", (int)size, m);\n\t\treturn NULL;\n\t}\n\tif (m->b_cont != NULL) {\n\t\tortp_fatal(\"RTCP parser does not work on fragmented mblk_t. Use msgpullup() before to re-assemble the packet.\");\n\t\treturn NULL;\n\t}\n\tch = (rtcp_common_header_t *)m->b_rptr;\n\treturn ch;\n}\n\nbool_t rtcp_is_SR(const mblk_t *m) {\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\tif (ch != NULL && rtcp_common_header_get_packet_type(ch) == RTCP_SR) {\n\t\tif (msgdsize(m) < (sizeof(rtcp_sr_t) - sizeof(report_block_t))) {\n\t\t\tortp_warning(\"Too short RTCP SR packet.\");\n\t\t\treturn FALSE;\n\t\t}\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n}\n\n/*Sender Report accessors */\nuint32_t rtcp_SR_get_ssrc(const mblk_t *m) {\n\trtcp_sr_t *sr = (rtcp_sr_t *)m->b_rptr;\n\treturn ntohl(sr->ssrc);\n}\n\nconst sender_info_t *rtcp_SR_get_sender_info(const mblk_t *m) {\n\trtcp_sr_t *sr = (rtcp_sr_t *)m->b_rptr;\n\treturn &sr->si;\n}\n\nconst report_block_t *rtcp_SR_get_report_block(const mblk_t *m, int idx) {\n\trtcp_sr_t *sr = (rtcp_sr_t *)m->b_rptr;\n\treport_block_t *rb = &sr->rb[idx];\n\tsize_t size = rtcp_get_size(m);\n\tif (((uint8_t *)rb) + sizeof(report_block_t) <= m->b_rptr + size) {\n\t\treturn rb;\n\t} else {\n\t\tif (idx < rtcp_common_header_get_rc(&sr->ch)) {\n\t\t\tortp_warning(\"RTCP packet should include a report_block_t at pos %i but has no space for it.\", idx);\n\t\t}\n\t}\n\treturn NULL;\n}\n\n/*Receiver report accessors*/\nbool_t rtcp_is_RR(const mblk_t *m) {\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\tif (ch != NULL && rtcp_common_header_get_packet_type(ch) == RTCP_RR) {\n\t\tif (msgdsize(m) < sizeof(rtcp_rr_t)) {\n\t\t\tortp_warning(\"Too short RTCP RR packet.\");\n\t\t\treturn FALSE;\n\t\t}\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n}\n\nuint32_t rtcp_RR_get_ssrc(const mblk_t *m) {\n\trtcp_rr_t *rr = (rtcp_rr_t *)m->b_rptr;\n\treturn ntohl(rr->ssrc);\n}\n\nconst report_block_t *rtcp_RR_get_report_block(const mblk_t *m, int idx) {\n\trtcp_rr_t *rr = (rtcp_rr_t *)m->b_rptr;\n\treport_block_t *rb = &rr->rb[idx];\n\tsize_t size = rtcp_get_size(m);\n\tif (((uint8_t *)rb) + sizeof(report_block_t) <= (m->b_rptr + size)) {\n\t\treturn rb;\n\t} else {\n\t\tif (idx < rtcp_common_header_get_rc(&rr->ch)) {\n\t\t\tortp_warning(\"RTCP packet should include a report_block_t at pos %i but has no space for it.\", idx);\n\t\t}\n\t}\n\treturn NULL;\n}\n\n/*SDES accessors */\nbool_t rtcp_is_SDES(const mblk_t *m) {\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\tif (ch && rtcp_common_header_get_packet_type(ch) == RTCP_SDES) {\n\t\tif (msgdsize(m) < rtcp_get_size(m)) {\n\t\t\tortp_warning(\"Too short RTCP SDES packet.\");\n\t\t\treturn FALSE;\n\t\t}\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n}\n\nvoid rtcp_sdes_parse(const mblk_t *m, SdesItemFoundCallback cb, void *user_data) {\n\tuint8_t *rptr = (uint8_t *)m->b_rptr + sizeof(rtcp_common_header_t);\n\tconst rtcp_common_header_t *ch = (rtcp_common_header_t *)m->b_rptr;\n\tuint8_t *end = rptr + (4 * (rtcp_common_header_get_length(ch) + 1));\n\tuint32_t ssrc = 0;\n\tint nchunk = 0;\n\tbool_t chunk_start = TRUE;\n\n\tif (end > (uint8_t *)m->b_wptr) end = (uint8_t *)m->b_wptr;\n\n\twhile (rptr < end) {\n\t\tif (chunk_start) {\n\t\t\tif (rptr + 4 <= end) {\n\t\t\t\tssrc = ntohl(*(uint32_t *)rptr);\n\t\t\t\trptr += 4;\n\t\t\t} else {\n\t\t\t\tortp_warning(\"incorrect chunk start in RTCP SDES\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tchunk_start = FALSE;\n\t\t} else {\n\t\t\tif (rptr + 2 <= end) {\n\t\t\t\tuint8_t type = rptr[0];\n\t\t\t\tuint8_t len = rptr[1];\n\n\t\t\t\tif (type == RTCP_SDES_END) {\n\t\t\t\t\t/* pad to next 32bit boundary*/\n\t\t\t\t\trptr = (uint8_t *)((intptr_t)(rptr + 4) & ~0x3);\n\t\t\t\t\tnchunk++;\n\t\t\t\t\tif (nchunk < rtcp_common_header_get_rc(ch)) {\n\t\t\t\t\t\tchunk_start = TRUE;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} else break;\n\t\t\t\t}\n\t\t\t\trptr += 2;\n\t\t\t\tif (rptr + len <= end) {\n\t\t\t\t\tcb(user_data, ssrc, type, (char *)rptr, len);\n\t\t\t\t\trptr += len;\n\t\t\t\t} else {\n\t\t\t\t\tortp_warning(\"bad item length in RTCP SDES\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t/*end of packet */\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*BYE accessors */\nbool_t rtcp_is_BYE(const mblk_t *m) {\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\tif (ch && rtcp_common_header_get_packet_type(ch) == RTCP_BYE) {\n\t\tif (msgdsize(m) < rtcp_get_size(m)) {\n\t\t\tortp_warning(\"Too short RTCP BYE packet.\");\n\t\t\treturn FALSE;\n\t\t}\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n}\n\nbool_t rtcp_BYE_get_ssrc(const mblk_t *m, int idx, uint32_t *ssrc) {\n\trtcp_bye_t *bye = (rtcp_bye_t *)m->b_rptr;\n\tint rc = rtcp_common_header_get_rc(&bye->ch);\n\tif (idx < rc) {\n\t\tif ((uint8_t *)&bye->ssrc[idx] <= (m->b_rptr + rtcp_get_size(m) - 4)) {\n\t\t\t*ssrc = ntohl(bye->ssrc[idx]);\n\t\t\treturn TRUE;\n\t\t} else {\n\t\t\tortp_warning(\"RTCP BYE should contain %i ssrc, but there is not enough room for it.\", rc);\n\t\t}\n\t}\n\treturn FALSE;\n}\n\nbool_t rtcp_BYE_get_reason(const mblk_t *m, const char **reason, int *reason_len) {\n\trtcp_bye_t *bye = (rtcp_bye_t *)m->b_rptr;\n\tint rc = rtcp_common_header_get_rc(&bye->ch);\n\tuint8_t *rptr = (uint8_t *)m->b_rptr + sizeof(rtcp_common_header_t) + rc * 4;\n\tuint8_t *end = (uint8_t *)(m->b_rptr + rtcp_get_size(m));\n\tif (rptr < end) {\n\t\tuint8_t content_len = rptr[0];\n\t\tif (rptr + 1 + content_len <= end) {\n\t\t\t*reason = (char *)rptr + 1;\n\t\t\t*reason_len = content_len;\n\t\t\treturn TRUE;\n\t\t} else {\n\t\t\tortp_warning(\"RTCP BYE has not enough space for reason phrase.\");\n\t\t\treturn FALSE;\n\t\t}\n\t}\n\treturn FALSE;\n}\n\n/*APP accessors */\nbool_t rtcp_is_APP(const mblk_t *m) {\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\tsize_t size = rtcp_get_size(m);\n\tif (ch != NULL && rtcp_common_header_get_packet_type(ch) == RTCP_APP) {\n\t\tif (msgdsize(m) < size) {\n\t\t\tortp_warning(\"Too short RTCP APP packet.\");\n\t\t\treturn FALSE;\n\t\t}\n\t\tif (size < sizeof(rtcp_app_t)) {\n\t\t\tortp_warning(\"Bad RTCP APP packet.\");\n\t\t\treturn FALSE;\n\t\t}\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n}\n\nint rtcp_APP_get_subtype(const mblk_t *m) {\n\trtcp_app_t *app = (rtcp_app_t *)m->b_rptr;\n\treturn rtcp_common_header_get_rc(&app->ch);\n}\n\nuint32_t rtcp_APP_get_ssrc(const mblk_t *m) {\n\trtcp_app_t *app = (rtcp_app_t *)m->b_rptr;\n\treturn ntohl(app->ssrc);\n}\n/* name argument is supposed to be at least 4 characters (note: no '\\0' written)*/\nvoid rtcp_APP_get_name(const mblk_t *m, char *name) {\n\trtcp_app_t *app = (rtcp_app_t *)m->b_rptr;\n\tmemcpy(name, app->name, 4);\n}\n/* retrieve the data. when returning, data points directly into the mblk_t */\nvoid rtcp_APP_get_data(const mblk_t *m, uint8_t **data, int *len) {\n\tint datalen = (int)rtcp_get_size(m) - sizeof(rtcp_app_t);\n\tif (datalen > 0) {\n\t\t*data = (uint8_t *)m->b_rptr + sizeof(rtcp_app_t);\n\t\t*len = datalen;\n\t} else {\n\t\t*len = 0;\n\t\t*data = NULL;\n\t}\n}\n\n/* RTCP XR accessors */\nbool_t rtcp_is_XR(const mblk_t *m) {\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\tif ((ch != NULL) && (rtcp_common_header_get_packet_type(ch) == RTCP_XR)) {\n\t\tif (msgdsize(m) < MIN_RTCP_XR_PACKET_SIZE) {\n\t\t\tortp_warning(\"Too short RTCP XR packet.\");\n\t\t\treturn FALSE;\n\t\t}\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n}\n\nrtcp_xr_block_type_t rtcp_XR_get_block_type(const mblk_t *m) {\n\trtcp_xr_generic_block_header_t *bh = (rtcp_xr_generic_block_header_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn bh->bt;\n}\n\nuint32_t rtcp_XR_get_ssrc(const mblk_t *m) {\n\trtcp_xr_header_t *xh = (rtcp_xr_header_t *)m->b_rptr;\n\treturn ntohl(xh->ssrc);\n}\n\nuint64_t rtcp_XR_rcvr_rtt_get_ntp_timestamp(const mblk_t *m) {\n\tuint64_t ts = 0;\n\trtcp_xr_rcvr_rtt_report_block_t *b = (rtcp_xr_rcvr_rtt_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\tts = ntohl(b->ntp_timestamp_msw);\n\tts <<= 32;\n\tts |= ntohl(b->ntp_timestamp_lsw);\n\treturn ts;\n}\n\nuint32_t rtcp_XR_dlrr_get_ssrc(const mblk_t *m) {\n\trtcp_xr_dlrr_report_subblock_t *b = (rtcp_xr_dlrr_report_subblock_t *)(m->b_rptr + sizeof(rtcp_xr_header_t) +\n\t                                                                       sizeof(rtcp_xr_generic_block_header_t));\n\treturn ntohl(b->ssrc);\n}\n\nuint32_t rtcp_XR_dlrr_get_lrr(const mblk_t *m) {\n\trtcp_xr_dlrr_report_subblock_t *b = (rtcp_xr_dlrr_report_subblock_t *)(m->b_rptr + sizeof(rtcp_xr_header_t) +\n\t                                                                       sizeof(rtcp_xr_generic_block_header_t));\n\treturn ntohl(b->lrr);\n}\n\nuint32_t rtcp_XR_dlrr_get_dlrr(const mblk_t *m) {\n\trtcp_xr_dlrr_report_subblock_t *b = (rtcp_xr_dlrr_report_subblock_t *)(m->b_rptr + sizeof(rtcp_xr_header_t) +\n\t                                                                       sizeof(rtcp_xr_generic_block_header_t));\n\treturn ntohl(b->dlrr);\n}\n\nuint8_t rtcp_XR_stat_summary_get_flags(const mblk_t *m) {\n\trtcp_xr_generic_block_header_t *bh = (rtcp_xr_generic_block_header_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn bh->flags;\n}\n\nuint32_t rtcp_XR_stat_summary_get_ssrc(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohl(b->ssrc);\n}\n\nuint16_t rtcp_XR_stat_summary_get_begin_seq(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohs(b->begin_seq);\n}\n\nuint16_t rtcp_XR_stat_summary_get_end_seq(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohs(b->end_seq);\n}\n\nuint32_t rtcp_XR_stat_summary_get_lost_packets(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohl(b->lost_packets);\n}\n\nuint32_t rtcp_XR_stat_summary_get_dup_packets(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohl(b->dup_packets);\n}\n\nuint32_t rtcp_XR_stat_summary_get_min_jitter(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohl(b->min_jitter);\n}\n\nuint32_t rtcp_XR_stat_summary_get_max_jitter(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohl(b->max_jitter);\n}\n\nuint32_t rtcp_XR_stat_summary_get_mean_jitter(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohl(b->mean_jitter);\n}\n\nuint32_t rtcp_XR_stat_summary_get_dev_jitter(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohl(b->dev_jitter);\n}\n\nuint8_t rtcp_XR_stat_summary_get_min_ttl_or_hl(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->min_ttl_or_hl;\n}\n\nuint8_t rtcp_XR_stat_summary_get_max_ttl_or_hl(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->max_ttl_or_hl;\n}\n\nuint8_t rtcp_XR_stat_summary_get_mean_ttl_or_hl(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->mean_ttl_or_hl;\n}\n\nuint8_t rtcp_XR_stat_summary_get_dev_ttl_or_hl(const mblk_t *m) {\n\trtcp_xr_stat_summary_report_block_t *b =\n\t    (rtcp_xr_stat_summary_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->dev_ttl_or_hl;\n}\n\nuint32_t rtcp_XR_voip_metrics_get_ssrc(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohl(b->ssrc);\n}\n\nuint8_t rtcp_XR_voip_metrics_get_loss_rate(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->loss_rate;\n}\n\nuint8_t rtcp_XR_voip_metrics_get_discard_rate(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->discard_rate;\n}\n\nuint8_t rtcp_XR_voip_metrics_get_burst_density(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->burst_density;\n}\n\nuint8_t rtcp_XR_voip_metrics_get_gap_density(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->gap_density;\n}\n\nuint16_t rtcp_XR_voip_metrics_get_burst_duration(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohs(b->burst_duration);\n}\n\nuint16_t rtcp_XR_voip_metrics_get_gap_duration(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohs(b->gap_duration);\n}\n\nuint16_t rtcp_XR_voip_metrics_get_round_trip_delay(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohs(b->round_trip_delay);\n}\n\nuint16_t rtcp_XR_voip_metrics_get_end_system_delay(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohs(b->end_system_delay);\n}\n\nuint8_t rtcp_XR_voip_metrics_get_signal_level(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->signal_level;\n}\n\nuint8_t rtcp_XR_voip_metrics_get_noise_level(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->noise_level;\n}\n\nuint8_t rtcp_XR_voip_metrics_get_rerl(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->rerl;\n}\n\nuint8_t rtcp_XR_voip_metrics_get_gmin(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->gmin;\n}\n\nuint8_t rtcp_XR_voip_metrics_get_r_factor(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->r_factor;\n}\n\nuint8_t rtcp_XR_voip_metrics_get_ext_r_factor(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->ext_r_factor;\n}\n\nuint8_t rtcp_XR_voip_metrics_get_mos_lq(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->mos_lq;\n}\n\nuint8_t rtcp_XR_voip_metrics_get_mos_cq(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->mos_cq;\n}\n\nuint8_t rtcp_XR_voip_metrics_get_rx_config(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn b->rx_config;\n}\n\nuint16_t rtcp_XR_voip_metrics_get_jb_nominal(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohs(b->jb_nominal);\n}\n\nuint16_t rtcp_XR_voip_metrics_get_jb_maximum(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohs(b->jb_maximum);\n}\n\nuint16_t rtcp_XR_voip_metrics_get_jb_abs_max(const mblk_t *m) {\n\trtcp_xr_voip_metrics_report_block_t *b =\n\t    (rtcp_xr_voip_metrics_report_block_t *)(m->b_rptr + sizeof(rtcp_xr_header_t));\n\treturn ntohs(b->jb_abs_max);\n}\n\n/* RTCP RTPFB accessors */\nbool_t rtcp_is_RTPFB(const mblk_t *m) {\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\tif ((ch != NULL) && (rtcp_common_header_get_packet_type(ch) == RTCP_RTPFB)) {\n\t\tif (msgdsize(m) < MIN_RTCP_RTPFB_PACKET_SIZE) {\n\t\t\tortp_warning(\"Too short RTCP RTPFB packet.\");\n\t\t\treturn FALSE;\n\t\t}\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n}\n\n/* Same as rtcp_is_RTPFB but not needing msgpullup. To be used internally only. */\nbool_t rtcp_is_RTPFB_internal(const mblk_t *m) {\n\trtcp_common_header_t *ch = (rtcp_common_header_t *)m->b_rptr;\n\treturn (rtcp_common_header_get_packet_type(ch) == RTCP_RTPFB) ? TRUE : FALSE;\n}\n\nrtcp_rtpfb_type_t rtcp_RTPFB_get_type(const mblk_t *m) {\n\trtcp_common_header_t *ch = (rtcp_common_header_t *)m->b_rptr;\n\treturn (rtcp_rtpfb_type_t)rtcp_common_header_get_rc(ch);\n}\n\nuint32_t rtcp_RTPFB_get_packet_sender_ssrc(const mblk_t *m) {\n\trtcp_fb_header_t *fbh = (rtcp_fb_header_t *)(m->b_rptr + sizeof(rtcp_common_header_t));\n\treturn ntohl(fbh->packet_sender_ssrc);\n}\n\nuint32_t rtcp_RTPFB_get_media_source_ssrc(const mblk_t *m) {\n\trtcp_fb_header_t *fbh = (rtcp_fb_header_t *)(m->b_rptr + sizeof(rtcp_common_header_t));\n\treturn ntohl(fbh->media_source_ssrc);\n}\n\nrtcp_fb_generic_nack_fci_t *rtcp_RTPFB_generic_nack_get_fci(const mblk_t *m) {\n\tsize_t size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + sizeof(rtcp_fb_generic_nack_fci_t);\n\tsize_t rtcp_size = rtcp_get_size(m);\n\tif (size > rtcp_size) {\n\t\treturn NULL;\n\t}\n\treturn (rtcp_fb_generic_nack_fci_t *)(m->b_rptr + size - sizeof(rtcp_fb_generic_nack_fci_t));\n}\n\nrtcp_fb_tmmbr_fci_t *rtcp_RTPFB_tmmbr_get_fci(const mblk_t *m) {\n\tsize_t size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + sizeof(rtcp_fb_tmmbr_fci_t);\n\tsize_t rtcp_size = rtcp_get_size(m);\n\tif (size > rtcp_size) {\n\t\treturn NULL;\n\t}\n\treturn (rtcp_fb_tmmbr_fci_t *)(m->b_rptr + size - sizeof(rtcp_fb_tmmbr_fci_t));\n}\n\nuint64_t rtcp_RTPFB_tmmbr_get_max_bitrate(const mblk_t *m) {\n\trtcp_fb_tmmbr_fci_t *fci = rtcp_RTPFB_tmmbr_get_fci(m);\n\treturn rtcp_fb_tmmbr_fci_get_mxtbr_mantissa(fci) * (1 << rtcp_fb_tmmbr_fci_get_mxtbr_exp(fci));\n}\n\nrtcp_fb_goog_remb_fci_t *rtcp_PSFB_goog_remb_get_fci(const mblk_t *m) {\n\tsize_t size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + sizeof(rtcp_fb_goog_remb_fci_t);\n\tsize_t rtcp_size = rtcp_get_size(m);\n\tif (size > rtcp_size) {\n\t\treturn NULL;\n\t}\n\treturn (rtcp_fb_goog_remb_fci_t *)(m->b_rptr + size - sizeof(rtcp_fb_goog_remb_fci_t));\n}\n\nuint64_t rtcp_PSFB_goog_remb_get_max_bitrate(const mblk_t *m) {\n\trtcp_fb_goog_remb_fci_t *fci = rtcp_PSFB_goog_remb_get_fci(m);\n\treturn rtcp_fb_goog_remb_fci_get_mxtbr_mantissa(fci) * (1 << rtcp_fb_goog_remb_fci_get_mxtbr_exp(fci));\n}\n\nbool_t rtcp_PSFB_is_goog_remb(const mblk_t *m) {\n\tconst rtcp_fb_goog_remb_fci_t *remb_fci = rtcp_PSFB_goog_remb_get_fci(m);\n\treturn (remb_fci != NULL && ntohl(remb_fci->identifier) == 0x52454d42);\n}\n\n/* RTCP PSFB accessors */\nbool_t rtcp_is_PSFB(const mblk_t *m) {\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\tif ((ch != NULL) && (rtcp_common_header_get_packet_type(ch) == RTCP_PSFB)) {\n\t\tif (msgdsize(m) < MIN_RTCP_PSFB_PACKET_SIZE) {\n\t\t\tortp_warning(\"Too short RTCP PSFB packet.\");\n\t\t\treturn FALSE;\n\t\t}\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n}\n\n/* Same as rtcp_is_PSFB but not needing msgpullup. To be used internally only. */\nbool_t rtcp_is_PSFB_internal(const mblk_t *m) {\n\trtcp_common_header_t *ch = (rtcp_common_header_t *)m->b_rptr;\n\treturn (rtcp_common_header_get_packet_type(ch) == RTCP_PSFB) ? TRUE : FALSE;\n}\n\nrtcp_psfb_type_t rtcp_PSFB_get_type(const mblk_t *m) {\n\trtcp_common_header_t *ch = (rtcp_common_header_t *)m->b_rptr;\n\treturn (rtcp_psfb_type_t)rtcp_common_header_get_rc(ch);\n}\n\nuint32_t rtcp_PSFB_get_packet_sender_ssrc(const mblk_t *m) {\n\trtcp_fb_header_t *fbh = (rtcp_fb_header_t *)(m->b_rptr + sizeof(rtcp_common_header_t));\n\treturn ntohl(fbh->packet_sender_ssrc);\n}\n\nuint32_t rtcp_PSFB_get_media_source_ssrc(const mblk_t *m) {\n\trtcp_fb_header_t *fbh = (rtcp_fb_header_t *)(m->b_rptr + sizeof(rtcp_common_header_t));\n\treturn ntohl(fbh->media_source_ssrc);\n}\n\nrtcp_fb_fir_fci_t *rtcp_PSFB_fir_get_fci(const mblk_t *m, unsigned int idx) {\n\tsize_t size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + ((idx + 1) * sizeof(rtcp_fb_fir_fci_t));\n\tsize_t rtcp_size = rtcp_get_size(m);\n\tif (size > rtcp_size) {\n\t\treturn NULL;\n\t}\n\treturn (rtcp_fb_fir_fci_t *)(m->b_rptr + size - sizeof(rtcp_fb_fir_fci_t));\n}\n\nrtcp_fb_sli_fci_t *rtcp_PSFB_sli_get_fci(const mblk_t *m, unsigned int idx) {\n\tsize_t size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + ((idx + 1) * sizeof(rtcp_fb_sli_fci_t));\n\tsize_t rtcp_size = rtcp_get_size(m);\n\tif (size > rtcp_size) {\n\t\treturn NULL;\n\t}\n\treturn (rtcp_fb_sli_fci_t *)(m->b_rptr + size - sizeof(rtcp_fb_sli_fci_t));\n}\n\nrtcp_fb_rpsi_fci_t *rtcp_PSFB_rpsi_get_fci(const mblk_t *m) {\n\treturn (rtcp_fb_rpsi_fci_t *)(m->b_rptr + sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t));\n}\n\nuint16_t rtcp_PSFB_rpsi_get_fci_bit_string_len(const mblk_t *m) {\n\trtcp_fb_rpsi_fci_t *fci = rtcp_PSFB_rpsi_get_fci(m);\n\tuint16_t bit_string_len_in_bytes =\n\t    (uint16_t)(rtcp_get_size(m) - (sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t) + 2));\n\treturn ((bit_string_len_in_bytes * 8) - fci->pb);\n}\n"
  },
  {
    "path": "src/rtpaudiolevel.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"ortp/logging.h\"\n#include \"ortp/rtp.h\"\n\n/**\n * Add the client to mixer audio level header extension.\n * See https://tools.ietf.org/html/rfc6464\n * @param packet the RTP packet.\n * @param id the identifier of the client to mixer audio level extension.\n * @param voice_activity TRUE if there is voice activity, FALSE otherwise.\n * @param audio_level the audio level to set expressed in dBov.\n **/\nvoid rtp_add_client_to_mixer_audio_level(mblk_t *packet, int id, bool_t voice_activity, int audio_level) {\n\tuint8_t data = (voice_activity ? 0x1 : 0x0) << 7 | (audio_level * -1);\n\trtp_add_extension_header(packet, id, sizeof(data), &data);\n}\n\n/**\n * Obtain the client to mixer audio level through the header extension.\n * See https://tools.ietf.org/html/rfc6464\n * @param packet the RTP packet.\n * @param id the identifier of the client to mixer audio level extension.\n * @param voice_activity set to TRUE if there is voice activity, FALSE otherwise\n * @return the client to mixer audio level in dBov, RTP_AUDIO_LEVEL_NO_VOLUME if there is no extension header or the\n * extension was not found.\n **/\nint rtp_get_client_to_mixer_audio_level(mblk_t *packet, int id, bool_t *voice_activity) {\n\tuint8_t *data;\n\tint ret;\n\n\tret = rtp_get_extension_header(packet, id, &data);\n\tif (ret != -1) {\n\t\t*voice_activity = (int)(*data) >> 7 & 0x1 ? TRUE : FALSE;\n\n\t\treturn (int)(*data & 0x7F) * -1; // Audio level is stored as a 7-bit number expressed in -dBov\n\t}\n\n\treturn RTP_AUDIO_LEVEL_NO_VOLUME;\n}\n\nstatic void rtp_add_mixer_to_client_audio_level_base(\n    mblk_t *packet, int id, size_t size, const rtp_audio_level_t *audio_levels, bool_t allocate_buffer) {\n\tuint8_t *data;\n\tint i;\n\n\tif (audio_levels == NULL || size <= 0) return;\n\n\tif (allocate_buffer != FALSE) {\n\t\t// Increase packet size to have enough space to add csrc. Make room just after the header and possible CSRC\n\t\t// already there but before possible extension header\n\t\tmsgpullup_with_insert(packet, RTP_FIXED_HEADER_SIZE + (rtp_get_cc(packet) * sizeof(uint32_t)),\n\t\t                      size * sizeof(uint32_t));\n\t}\n\n\tdata = ortp_new0(uint8_t, size);\n\tfor (i = 0; i < (int)size; i++) {\n\t\trtp_add_csrc(packet, audio_levels[i].csrc);\n\t\tdata[i] = 0x0 << 7 | (audio_levels[i].dbov * -1);\n\t}\n\tif (allocate_buffer != FALSE) {\n\t\trtp_add_extension_header(packet, id, size, data);\n\t} else {\n\t\trtp_write_extension_header(packet, id, size, data);\n\t}\n\n\tortp_free(data);\n}\n\n/**\n * Write the mixer to client audio level header extension, do not manage memory\n * just write the header, space for it must already be allocated, any data present there is overwritten\n * csrc are written directly after the header\n * See https://tools.ietf.org/html/rfc6465\n * @param packet the RTP packet.\n * @param id the identifier of the client to mixer audio level extension.\n * @param size the size of the audio_levels list.\n * @param audio_levels the list if audio levels to set.\n **/\nvoid rtp_write_mixer_to_client_audio_level(mblk_t *packet, int id, size_t size, const rtp_audio_level_t *audio_levels) {\n\trtp_add_mixer_to_client_audio_level_base(packet, id, size, audio_levels, FALSE);\n}\n/**\n * Add the mixer to client audio level header extension.\n * packet size is increased to fit the new data, rptr/wptr might be reallocated\n * See https://tools.ietf.org/html/rfc6465\n * @param packet the RTP packet.\n * @param id the identifier of the client to mixer audio level extension.\n * @param size the size of the audio_levels list.\n * @param audio_levels the list if audio levels to set.\n **/\nvoid rtp_add_mixer_to_client_audio_level(mblk_t *packet, int id, size_t size, const rtp_audio_level_t *audio_levels) {\n\trtp_add_mixer_to_client_audio_level_base(packet, id, size, audio_levels, TRUE);\n}\n\n/**\n * Obtain the mixer to client audio level through the header extension.\n * See https://tools.ietf.org/html/rfc6465\n * @param packet the RTP packet.\n * @param id the identifier of the mixer to client audio level extension.\n * @param audio_levels the list of mixer to client audio levels, this array must be allocated before calling this\n *function.\n * @return the size of the mixer to client audio levels list, -1 in case of error.\n **/\nint rtp_get_mixer_to_client_audio_level(mblk_t *packet, int id, rtp_audio_level_t *audio_levels) {\n\tint ret, i;\n\tuint8_t *data;\n\n\tret = rtp_get_extension_header(packet, id, &data);\n\tif (ret != -1) {\n\t\trtp_header_t *header = (rtp_header_t *)packet->b_rptr;\n\n\t\tif (ret != header->cc) {\n\t\t\tortp_error(\n\t\t\t    \"Error while retrieving mixer to client audio levels [%p]: number of audio level and csrc do not match\",\n\t\t\t    packet);\n\t\t\treturn -1;\n\t\t}\n\n\t\tfor (i = 0; i < ret; i++) {\n\t\t\taudio_levels[i].csrc = rtp_header_get_csrc(header, i);\n\t\t\taudio_levels[i].dbov = (int)(data[i] & 0x7F) * -1;\n\t\t}\n\t}\n\n\treturn ret;\n}\n"
  },
  {
    "path": "src/rtpbundle.cc",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <algorithm>\n\n#include <bctoolbox/defs.h>\n\n#include \"ortp/logging.h\"\n#include \"ortp/rtpsession.h\"\n#include \"rtpbundle.h\"\n#include \"rtpsession_priv.h\"\n\n// C - Interface\n\nextern \"C\" RtpBundle *rtp_bundle_new(void) {\n\treturn reinterpret_cast<RtpBundle *>(new RtpBundleCxx());\n}\n\nextern \"C\" void rtp_bundle_delete(RtpBundle *bundle) {\n\tdelete reinterpret_cast<RtpBundleCxx *>(bundle);\n}\n\nextern \"C\" int rtp_bundle_get_mid_extension_id(RtpBundle *bundle) {\n\treturn reinterpret_cast<RtpBundleCxx *>(bundle)->getMidId();\n}\n\nextern \"C\" void rtp_bundle_set_mid_extension_id(RtpBundle *bundle, int id) {\n\treinterpret_cast<RtpBundleCxx *>(bundle)->setMidId(id);\n}\n\nextern \"C\" void rtp_bundle_add_session(RtpBundle *bundle, const char *mid, RtpSession *session) {\n\treinterpret_cast<RtpBundleCxx *>(bundle)->addSession(mid, session);\n}\n\nextern \"C\" void rtp_bundle_remove_sessions_by_id(RtpBundle *bundle, const char *mid) {\n\treinterpret_cast<RtpBundleCxx *>(bundle)->removeSessions(mid);\n}\n\nextern \"C\" void rtp_bundle_remove_session(RtpBundle *bundle, RtpSession *session) {\n\treinterpret_cast<RtpBundleCxx *>(bundle)->removeSession(session);\n}\n\nextern \"C\" void rtp_bundle_clear(RtpBundle *bundle) {\n\treinterpret_cast<RtpBundleCxx *>(bundle)->clear();\n}\n\nextern \"C\" void rtp_bundle_session_mode_updated(RtpBundle *bundle, RtpSession *session, RtpSessionMode previous_mode) {\n\treinterpret_cast<RtpBundleCxx *>(bundle)->sessionModeUpdated(session, previous_mode);\n}\n\nextern \"C\" RtpSession *rtp_bundle_get_primary_session(RtpBundle *bundle) {\n\treturn reinterpret_cast<RtpBundleCxx *>(bundle)->getPrimarySession();\n}\n\nextern \"C\" void rtp_bundle_set_primary_session(RtpBundle *bundle, RtpSession *session) {\n\treinterpret_cast<RtpBundleCxx *>(bundle)->setPrimarySession(session);\n}\n\nextern \"C\" char *rtp_bundle_get_session_mid(RtpBundle *bundle, RtpSession *session) {\n\ttry {\n\t\tauto &mid = reinterpret_cast<RtpBundleCxx *>(bundle)->getSessionMid(session);\n\t\treturn bctbx_strdup(mid.c_str());\n\t} catch (std::string const &e) {\n\t\tortp_warning(\"RtpBundle[%p]: cannot get mid for session (%p): %s\", bundle, session, e.c_str());\n\t\treturn nullptr;\n\t}\n}\n\nextern \"C\" mblk_t *rtp_bundle_dispatch(RtpBundle *bundle, bool_t is_rtp, mblk_t *m) {\n\treturn reinterpret_cast<RtpBundleCxx *>(bundle)->dispatch(is_rtp, m).value_or(nullptr);\n}\n\nextern \"C\" RtpSession *rtp_bundle_lookup_session_for_outgoing_packet(RtpBundle *bundle, mblk_t *m) {\n\treturn reinterpret_cast<RtpBundleCxx *>(bundle)->checkForSession(m, true, true);\n}\n\n// C++ - Implementation\n\nRtpBundleCxx::~RtpBundleCxx() {\n\tclear();\n}\nint RtpBundleCxx::getMidId() const {\n\treturn mMidId;\n}\n\nvoid RtpBundleCxx::setMidId(int id) {\n\tmMidId = id;\n}\n\nvoid RtpBundleCxx::addSession(const std::string &mid, RtpSession *session) {\n\tconst std::lock_guard guard(mAssignmentMutex);\n\n\t// Search for the session in both maps to check if it hasn't already been inserted.\n\tif (findSession(session))\n\t\tortp_error(\"RtpBundle[%p]: Cannot add session (%p) as it is already in the bundle\", this, session);\n\n\t// Check for the mode. If SENDONLY, we already know it's SSRC, and we can assign it now.\n\t// Otherwise, add it in the waiting for assignment map.\n\tswitch (session->mode) {\n\t\tcase RTP_SESSION_SENDONLY:\n\t\t\tmSsrcToSession.emplace(session->snd.ssrc, BundleSession{{mid, 0}, session});\n\t\t\tbreak;\n\t\tcase RTP_SESSION_RECVONLY:\n\t\t\tmWaitingForAssignment.emplace(mid, session);\n\t\t\tbreak;\n\t\tcase RTP_SESSION_SENDRECV:\n\t\t\tmSsrcToSession.emplace(session->snd.ssrc, BundleSession{{mid, 0}, session});\n\t\t\tmWaitingForAssignment.emplace(mid, session);\n\t\t\tbreak;\n\t}\n\n\tif (!mPrimary) {\n\t\tmPrimary = session;\n\t\tsession->is_primary = TRUE;\n\t}\n\n\trtp_session_set_bundle(session, reinterpret_cast<RtpBundle *>(this));\n}\n\nbool RtpBundleCxx::findSession(RtpSession *session) const {\n\tconst auto inMainMap = std::find_if(\n\t    mSsrcToSession.begin(), mSsrcToSession.end(),\n\t    [session](const std::pair<uint32_t, BundleSession> &t) -> bool { return t.second.rtpSession == session; });\n\n\tif (inMainMap != mSsrcToSession.end()) {\n\t\treturn true;\n\t}\n\n\tconst auto inWaitingMap =\n\t    std::find_if(mWaitingForAssignment.begin(), mWaitingForAssignment.end(),\n\t                 [session](const std::pair<std::string, RtpSession *> &t) -> bool { return t.second == session; });\n\n\tif (inWaitingMap != mWaitingForAssignment.end()) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool RtpBundleCxx::findMid(const std::string &mid) const {\n\tconst auto inMainMap =\n\t    std::find_if(mSsrcToSession.begin(), mSsrcToSession.end(),\n\t                 [mid](const std::pair<uint32_t, BundleSession> &t) -> bool { return t.second.mid.mid == mid; });\n\n\tif (inMainMap != mSsrcToSession.end()) {\n\t\treturn true;\n\t}\n\n\tconst auto inWaitingMap =\n\t    std::find_if(mWaitingForAssignment.begin(), mWaitingForAssignment.end(),\n\t                 [mid](const std::pair<std::string, RtpSession *> &t) -> bool { return t.first == mid; });\n\n\tif (inWaitingMap != mWaitingForAssignment.end()) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid RtpBundleCxx::removeSessions(const std::string &mid) {\n\tconst std::lock_guard guard(mAssignmentMutex);\n\n\t// Remove the session from the main map.\n\tfor (auto it = mSsrcToSession.begin(); it != mSsrcToSession.end();) {\n\t\tif (it->second.mid.mid == mid) {\n\t\t\tclearSession(it->second.rtpSession);\n\t\t\tit = mSsrcToSession.erase(it);\n\t\t} else {\n\t\t\t++it;\n\t\t}\n\t}\n\n\t// Then check for sessions waiting for assignment.\n\t// mWaitingForAssignment is a multimap, we need to remove all RtpSessions pointed by the mid.\n\tif (const auto session = mWaitingForAssignment.find(mid); session != mWaitingForAssignment.end()) {\n\t\t// We have to remove the bundle from each RtpSession\n\t\tconst auto [first, last] = mWaitingForAssignment.equal_range(mid);\n\t\tfor (auto i = first; i != last; ++i) {\n\t\t\tclearSession(i->second);\n\t\t}\n\n\t\tmWaitingForAssignment.erase(mid);\n\t}\n\n\t// Finally remove the ssrcs associated.\n\tfor (auto it = mSsrcToMid.begin(); it != mSsrcToMid.end();) {\n\t\tif (it->second == mid) {\n\t\t\tit = mSsrcToMid.erase(it);\n\t\t} else {\n\t\t\t++it;\n\t\t}\n\t}\n}\n\nvoid RtpBundleCxx::removeSession(RtpSession *session) {\n\tconst std::lock_guard guard(mAssignmentMutex);\n\n\t// Remove the session from the main map.\n\tfor (auto it = mSsrcToSession.begin(); it != mSsrcToSession.end();) {\n\t\tif (it->second.rtpSession == session) {\n\t\t\tclearSession(it->second.rtpSession);\n\t\t\tit = mSsrcToSession.erase(it);\n\t\t} else {\n\t\t\t++it;\n\t\t}\n\t}\n\n\t// Remove the session from the assignment map.\n\tfor (auto it = mWaitingForAssignment.begin(); it != mWaitingForAssignment.end();) {\n\t\tif (it->second == session) {\n\t\t\tclearSession(it->second);\n\t\t\tit = mWaitingForAssignment.erase(it);\n\t\t} else {\n\t\t\t++it;\n\t\t}\n\t}\n}\n\nvoid RtpBundleCxx::clearSession(RtpSession *session) {\n\trtp_session_set_bundle(session, nullptr);\n\n\tif (mPrimary != nullptr) {\n\t\tif (session == mPrimary) {\n\t\t\tmPrimary->is_primary = FALSE;\n\t\t\tmPrimary = nullptr;\n\t\t} else {\n\t\t\t// Remove the session from all of mPrimary's signal tables.\n\t\t\tfor (const bctbx_list_t *it = mPrimary->signal_tables; it != nullptr; it = it->next) {\n\t\t\t\tconst auto t = static_cast<RtpSignalTable *>(it->data);\n\t\t\t\trtp_signal_table_remove_by_source_session(t, session);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid RtpBundleCxx::clear() {\n\tfor (const auto &entry : mSsrcToSession) {\n\t\tentry.second.rtpSession->bundle = nullptr;\n\t}\n\tmSsrcToSession.clear();\n\n\tfor (const auto &entry : mWaitingForAssignment) {\n\t\tentry.second->bundle = nullptr;\n\t}\n\tmWaitingForAssignment.clear();\n\n\tmSsrcToMid.clear();\n\tmPrimary = nullptr;\n}\n\nvoid RtpBundleCxx::sessionModeUpdated(RtpSession *session, RtpSessionMode previousMode) {\n\tconst RtpSessionMode newMode = session->mode;\n\tif (previousMode == newMode) return;\n\n\tconst std::lock_guard guard(mAssignmentMutex);\n\n\t// We only have to do something if the session goes from SENDONLY to another mode or the opposite\n\tif ((previousMode == RTP_SESSION_RECVONLY || previousMode == RTP_SESSION_SENDRECV) &&\n\t    newMode == RTP_SESSION_SENDONLY) {\n\t\tMid mid{};\n\n\t\tif (session->ssrc_set && session->rcv.ssrc != 0) {\n\t\t\t// Remove it from main map\n\t\t\tfor (auto it = mSsrcToSession.begin(); it != mSsrcToSession.end(); ++it) {\n\t\t\t\tif (it->second.rtpSession == session) {\n\t\t\t\t\tmid = it->second.mid;\n\t\t\t\t\tmSsrcToSession.erase(it);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Remove it from waiting map\n\t\t\tfor (auto it = mWaitingForAssignment.begin(); it != mWaitingForAssignment.end(); ++it) {\n\t\t\t\tif (it->second == session) {\n\t\t\t\t\tmid.mid = it->first;\n\t\t\t\t\tmWaitingForAssignment.erase(it);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add it to main map with send ssrc\n\t\tmSsrcToSession.emplace(session->snd.ssrc, BundleSession{mid, session});\n\n\t\tortp_message(\"RtpBundle[%p]: Session (%p) mode has been updated from %d to %d\", this, session, previousMode,\n\t\t             newMode);\n\t} else if (previousMode == RTP_SESSION_SENDONLY &&\n\t           (newMode == RTP_SESSION_RECVONLY || newMode == RTP_SESSION_SENDRECV)) {\n\t\tif (const auto it = mSsrcToSession.find(session->snd.ssrc); it != mSsrcToSession.end()) {\n\t\t\t// Remove it from main map\n\t\t\tconst auto mid = it->second.mid.mid;\n\t\t\tmSsrcToSession.erase(it);\n\n\t\t\t// Add it to waiting for assignment map\n\t\t\tmWaitingForAssignment.emplace(mid, session);\n\n\t\t\tortp_message(\"RtpBundle[%p]: Session (%p) mode has been updated from %d to %d\", this, session, previousMode,\n\t\t\t             newMode);\n\t\t}\n\t}\n}\n\nRtpSession *RtpBundleCxx::getPrimarySession() const {\n\treturn mPrimary;\n}\n\nvoid RtpBundleCxx::setPrimarySession(RtpSession *session) {\n\tif (findSession(session)) {\n\t\tif (mPrimary) {\n\t\t\tmPrimary->is_primary = FALSE;\n\t\t}\n\n\t\tmPrimary = session;\n\t\tmPrimary->is_primary = TRUE;\n\t}\n}\n\nconst std::string &RtpBundleCxx::getSessionMid(RtpSession *session) const {\n\tconst auto inMainMap = std::find_if(\n\t    mSsrcToSession.begin(), mSsrcToSession.end(),\n\t    [session](const std::pair<uint32_t, BundleSession> &t) -> bool { return t.second.rtpSession == session; });\n\n\tif (inMainMap != mSsrcToSession.end()) {\n\t\treturn inMainMap->second.mid.mid;\n\t}\n\n\tconst auto inWaitingMap =\n\t    std::find_if(mWaitingForAssignment.begin(), mWaitingForAssignment.end(),\n\t                 [session](const std::pair<std::string, RtpSession *> &t) -> bool { return t.second == session; });\n\n\tif (inWaitingMap != mWaitingForAssignment.end()) {\n\t\treturn inWaitingMap->first;\n\t}\n\n\tthrow std::string(\"the session must be in the bundle!\");\n}\n\nstatic void getSsrcFromSdes(void *userData,\n                            uint32_t ssrc,\n                            rtcp_sdes_type_t t,\n                            BCTBX_UNUSED(const char *content),\n                            BCTBX_UNUSED(uint8_t contentLen)) {\n\tconst auto value = static_cast<uint32_t *>(userData);\n\n\tif (*value == 0 || t == RTCP_SDES_MID) {\n\t\t*value = ssrc;\n\t}\n}\n\nstatic uint32_t getSsrcFromMessage(const mblk_t *m, bool isRtp) {\n\tif (isRtp) {\n\t\treturn rtp_get_ssrc(m);\n\t}\n\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\tuint32_t ssrc = 0;\n\n\tswitch (rtcp_common_header_get_packet_type(ch)) {\n\t\tcase RTCP_SR:\n\t\t\treturn rtcp_SR_get_ssrc(m);\n\t\tcase RTCP_RR:\n\t\t\treturn rtcp_RR_get_ssrc(m);\n\t\tcase RTCP_SDES:\n\t\t\trtcp_sdes_parse(m, getSsrcFromSdes, &ssrc);\n\t\t\treturn ssrc;\n\t\tcase RTCP_BYE:\n\t\t\tif (rtcp_BYE_get_ssrc(m, 0, &ssrc)) {\n\t\t\t\treturn ssrc;\n\t\t\t}\n\t\t\treturn -1;\n\t\tcase RTCP_APP:\n\t\t\treturn rtcp_APP_get_ssrc(m);\n\t\tcase RTCP_RTPFB:\n\t\t\treturn rtcp_RTPFB_get_packet_sender_ssrc(m);\n\t\tcase RTCP_PSFB:\n\t\t\treturn rtcp_PSFB_get_packet_sender_ssrc(m);\n\t\tcase RTCP_XR:\n\t\t\treturn rtcp_XR_get_ssrc(m);\n\t\tdefault:\n\t\t\tortp_warning(\"Unknown RTCP packet type (%u) while retrieving it's SSRC\",\n\t\t\t             rtcp_common_header_get_packet_type(ch));\n\t\t\tbreak;\n\t}\n\n\treturn -1;\n}\n\nstatic bool getRTCPReferedSSRC(const mblk_t *m, uint32_t *ssrc) {\n\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\tconst report_block_t *rb;\n\n\tswitch (rtcp_common_header_get_packet_type(ch)) {\n\t\tcase RTCP_SR:\n\t\t\tif (rtcp_common_header_get_rc(ch) != 1) return false;\n\t\t\trb = rtcp_SR_get_report_block(m, 0);\n\t\t\tif (rb) {\n\t\t\t\t*ssrc = report_block_get_ssrc(rb);\n\t\t\t\treturn *ssrc != 0;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase RTCP_RR:\n\t\t\tif (rtcp_common_header_get_rc(ch) != 1) return false;\n\t\t\trb = rtcp_RR_get_report_block(m, 0);\n\t\t\tif (rb) {\n\t\t\t\t*ssrc = report_block_get_ssrc(rb);\n\t\t\t\treturn *ssrc != 0;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase RTCP_APP:\n\t\tcase RTCP_SDES:\n\t\t\t// No referred SSRC in a SDES or APP\n\t\t\tbreak;\n\t\tcase RTCP_BYE:\n\t\t\tif (rtcp_common_header_get_rc(ch) != 1) return false;\n\t\t\tif (rtcp_BYE_get_ssrc(m, 0, ssrc)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase RTCP_RTPFB:\n\t\t\t*ssrc = rtcp_RTPFB_get_media_source_ssrc(m);\n\n\t\t\t// In case of TMMBR or TMMBN, media ssrc is always 0 but we can use the ssrc in the FCI field\n\t\t\tif (const auto type = rtcp_RTPFB_get_type(m);\n\t\t\t    *ssrc == 0 && (type == RTCP_RTPFB_TMMBR || type == RTCP_RTPFB_TMMBN)) {\n\t\t\t\tif (const auto *fci = rtcp_RTPFB_tmmbr_get_fci(m); fci != nullptr)\n\t\t\t\t\t*ssrc = rtcp_fb_tmmbr_fci_get_ssrc(fci);\n\t\t\t}\n\n\t\t\treturn *ssrc != 0;\n\t\tcase RTCP_PSFB:\n\t\t\t*ssrc = rtcp_PSFB_get_media_source_ssrc(m);\n\t\t\treturn *ssrc != 0;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\treturn false;\n}\n\nvoid RtpBundleCxx::checkForSessionSdesCallback(\n    void *userData, BCTBX_UNUSED(uint32_t ssrc), rtcp_sdes_type_t t, const char *content, uint8_t contentLen) {\n\tauto *bundle = static_cast<RtpBundleCxx *>(userData);\n\n\tif (t == RTCP_SDES_MID) {\n\t\tbundle->mSdesParseMid = std::string(content, contentLen);\n\t}\n}\n\nstd::string RtpBundleCxx::getMid(const mblk_t *m, bool isRtp) {\n\tif (isRtp && rtp_get_extbit(m)) {\n\t\tuint8_t *data;\n\t\tif (const size_t midSize = rtp_get_extension_header(m, mMidId != -1 ? mMidId : RTP_EXTENSION_MID, &data);\n\t\t    midSize != static_cast<size_t>(-1)) {\n\t\t\treturn {reinterpret_cast<char *>(data), midSize};\n\t\t}\n\t} else {\n\t\tif (rtcp_is_SDES(m)) {\n\t\t\t// The checkForSessionSdesCallback() checks for presence of mid.\n\t\t\tmSdesParseMid.clear();\n\t\t\trtcp_sdes_parse(m, checkForSessionSdesCallback, this);\n\n\t\t\treturn mSdesParseMid;\n\t\t}\n\t}\n\n\treturn \"\";\n}\n\nRtpBundleCxx::BundleSession *RtpBundleCxx::findReferredSession(const uint32_t referredSsrc) {\n\t// Check if we have an entry associated to this ssrc, this should be the case if the ssrc is from a SEND_ONLY\n\t// session.\n\tif (const auto it = mSsrcToSession.find(referredSsrc); it != mSsrcToSession.end()) {\n\t\treturn &it->second;\n\t}\n\n\t// Else check for a corresponding SENDRCV session with the send ssrc in the session map for this mid.\n\tfor (auto &[ssrc, session] : mSsrcToSession) {\n\t\tif (session.rtpSession->mode == RTP_SESSION_SENDRECV && session.rtpSession->snd.ssrc == referredSsrc) {\n\t\t\treturn &session;\n\t\t}\n\t}\n\n\treturn nullptr;\n}\n\nvoid RtpBundleCxx::updateBundleSession(BundleSession &session, const std::string &mid, uint32_t sequenceNumber) {\n\tif (!mid.empty() && mid != session.mid.mid && sequenceNumber > session.mid.sequenceNumber) {\n\t\tsession.mid.mid = mid;\n\t\tsession.mid.sequenceNumber = sequenceNumber;\n\t}\n}\n\nRtpSession *RtpBundleCxx::checkForSession(const mblk_t *m, bool isRtp, bool isOutgoing) {\n\tconst std::lock_guard guard(mAssignmentMutex);\n\n\t// STUN packet, return the primary session.\n\tif (isRtp && rtp_get_version(m) != 2) {\n\t\treturn mPrimary;\n\t}\n\n\tconst uint32_t ssrc = getSsrcFromMessage(m, isRtp);\n\tstd::string mid = getMid(m, isRtp);\n\n\t// If we are in RTCP and have a referred ssrc, try to route it to the correct session first.\n\tif (uint32_t referredSsrc; !isRtp && getRTCPReferedSSRC(m, &referredSsrc)) {\n\t\tif (auto *session = findReferredSession(referredSsrc); session != nullptr) {\n\t\t\tconst rtcp_common_header_t *ch = rtcp_get_common_header(m);\n\t\t\tortp_message(\"RtpBundle[%p]: RTCP msg (%d) referring to SSRC %u with sender-ssrc %u \"\n\t\t\t             \"routed to session %p\",\n\t\t\t             this, rtcp_common_header_get_packet_type(ch), referredSsrc, ssrc, session->rtpSession);\n\n\t\t\tupdateBundleSession(*session, mid, rtp_get_seqnumber(m));\n\t\t\treturn session->rtpSession;\n\t\t}\n\t}\n\n\t// Try to route the packet to the correct session.\n\tif (const auto it = mSsrcToSession.find(ssrc); it != mSsrcToSession.end()) {\n\t\tupdateBundleSession(it->second, mid, isRtp ? rtp_get_seqnumber(m) : 0);\n\t\treturn it->second.rtpSession;\n\t}\n\n\t// Retrieve or update the MID from the association map.\n\tif (const auto it = mSsrcToMid.find(ssrc); mid.empty()) {\n\t\t// If there is no mid in the packet, check if we have it stored for this ssrc.\n\t\tif (it == mSsrcToMid.end()) {\n\t\t\tortp_warning(\"RtpBundle[%p]: Packet with SSRC %u doesn't have any mid and no corresponding mid in bundle\",\n\t\t\t             this, ssrc);\n\t\t\treturn nullptr;\n\t\t}\n\n\t\tmid = it->second;\n\t} else if (it == mSsrcToMid.end()) {\n\t\t// We have a mid in the packet, but not in the map. Insert it.\n\t\tmSsrcToMid[ssrc] = mid;\n\t}\n\n\tif (!isOutgoing) {\n\t\t// If we are in RTP, check if we have a corresponding mid in the assignment map.\n\t\tif (isRtp) {\n\t\t\tconst auto [first, last] = mWaitingForAssignment.equal_range(mid);\n\n\t\t\tfor (auto s = first; s != last; ++s) {\n\t\t\t\tRtpSession *session = s->second;\n\n\t\t\t\t// Check if this blank session knows the payload type of the incoming packet.\n\t\t\t\tif (session->rcv.pt == rtp_get_payload_type(m)) {\n\t\t\t\t\tortp_message(\"RtpBundle[%p]: Assigning incoming SSRC %u to session %p using RTP with pt %d\", this,\n\t\t\t\t\t             ssrc, session, rtp_get_payload_type(m));\n\n\t\t\t\t\tsession->ssrc_set = TRUE;\n\t\t\t\t\tsession->rcv.ssrc = ssrc;\n\n\t\t\t\t\t// Assign the session to the incoming ssrc and remove this session from the assignment map.\n\t\t\t\t\tmSsrcToSession.emplace(ssrc, BundleSession{{mid, 0}, session});\n\t\t\t\t\tmWaitingForAssignment.erase(s);\n\n\t\t\t\t\treturn session;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Handle case where RTCP is received before RTP and the corresponding session is not yet assigned.\n\t\t\t// We do NOT assign it, but we still return the correct session.\n\t\t\tif (uint32_t referredSsrc; getRTCPReferedSSRC(m, &referredSsrc)) {\n\t\t\t\tconst auto [first, last] = mWaitingForAssignment.equal_range(mid);\n\n\t\t\t\tfor (auto s = first; s != last; ++s) {\n\t\t\t\t\tif (s->second->snd.ssrc == referredSsrc) return s->second;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// We have no existing RtpSession for this SSRC.\n\t// Invoke the callbacks to let the application layer decide what to do.\n\tRtpSession *newRtpSession = nullptr;\n\n\tif (isRtp && !mid.empty()) { // Do not create new session for unknown RTCP or when mid is unknown\n\t\tif (isOutgoing) {\n\t\t\tortp_message(\"RtpBundle[%p]: emit on_new_outgoing_ssrc_in_bundle on SSRC %u from session %p with pt %d\",\n\t\t\t             this, ssrc, getPrimarySession(), rtp_get_payload_type(m));\n\n\t\t\trtp_signal_table_emit3(&(getPrimarySession()->on_new_outgoing_ssrc_in_bundle), (void *)m, &newRtpSession);\n\n\t\t\tif (newRtpSession) {\n\t\t\t\tnewRtpSession->snd.ssrc = ssrc;\n\t\t\t}\n\t\t} else {\n\t\t\tortp_message(\"RtpBundle[%p]: emit on_new_incoming_ssrc_in_bundle on SSRC %u from session %p with pt %d\",\n\t\t\t             this, ssrc, getPrimarySession(), rtp_get_payload_type(m));\n\n\t\t\trtp_signal_table_emit3(&(getPrimarySession()->on_new_incoming_ssrc_in_bundle), (void *)m, &newRtpSession);\n\n\t\t\tif (newRtpSession) {\n\t\t\t\t// If the current rcv.ssrc is set to another ssrc and has an entry in the association map, remove it\n\t\t\t\t// first. This is in case the newRtpSession is an existing session being reused.\n\t\t\t\tif (newRtpSession->ssrc_set && newRtpSession->rcv.ssrc != ssrc &&\n\t\t\t\t    mSsrcToSession.find(newRtpSession->rcv.ssrc) != mSsrcToSession.end()) {\n\t\t\t\t\tmSsrcToSession.erase(newRtpSession->rcv.ssrc);\n\t\t\t\t}\n\n\t\t\t\t// The new session is associated to the incoming SSRC.\n\t\t\t\tnewRtpSession->ssrc_set = TRUE;\n\t\t\t\tnewRtpSession->rcv.ssrc = ssrc;\n\t\t\t}\n\t\t}\n\n\t\tif (newRtpSession) {\n\t\t\t// We do not use addSession as we already know it's ssrc\n\t\t\tmSsrcToSession.emplace(isOutgoing ? newRtpSession->snd.ssrc : newRtpSession->rcv.ssrc,\n\t\t\t                       BundleSession{{mid, 0}, newRtpSession});\n\t\t\tif (newRtpSession->bundle == nullptr)\n\t\t\t\trtp_session_set_bundle(newRtpSession, reinterpret_cast<RtpBundle *>(this));\n\t\t}\n\t}\n\n\treturn newRtpSession;\n}\n\nstd::optional<mblk_t *> RtpBundleCxx::dispatch(bool isRtp, mblk_t *m) {\n\tif (isRtp) return dispatchRtpMessage(m);\n\n\treturn dispatchRtcpMessage(m);\n}\n\nstd::optional<mblk_t *> RtpBundleCxx::dispatchRtpMessage(mblk_t *m) {\n\tRtpSession *session = checkForSession(m, true);\n\tif (session == nullptr) {\n\t\tfreemsg(m);\n\t\treturn {};\n\t}\n\n\tif (session != mPrimary) {\n\t\tortp_mutex_lock(&session->rtp.gs.bundleq_lock);\n\t\tputq(&session->rtp.gs.bundleq, m);\n\t\tortp_mutex_unlock(&session->rtp.gs.bundleq_lock);\n\t\treturn {};\n\t}\n\n\treturn m;\n}\n\nstd::optional<mblk_t *> RtpBundleCxx::dispatchRtcpMessage(mblk_t *m) {\n\tmblk_t *mPrimarymsg = nullptr;\n\n\t// Check if the packet contains a SDES first\n\tRtcpParserContext rtcp_parser_ctx;\n\tconst mblk_t *m_rtcp = rtcp_parser_context_init(&rtcp_parser_ctx, m);\n\tdo {\n\t\tif (rtcp_is_SDES(m_rtcp)) {\n\t\t\t// call checkForSession that will update the mid table\n\t\t\tcheckForSession(m_rtcp, false);\n\t\t}\n\t} while ((m_rtcp = rtcp_parser_context_next_packet(&rtcp_parser_ctx)) != nullptr);\n\n\t// Now go through the compound RTCP packet and dispatch each of its elements in streams.\n\t// In order to avoid unnecessary split between SR and SDES of a same compound packet,\n\t// each RTCP element belonging to same stream are aggregated.\n\tm_rtcp = rtcp_parser_context_start(&rtcp_parser_ctx);\n\tstd::map<RtpSession *, mblk_t *> dispatchMap;\n\tdo {\n\t\tmblk_t *tmp = dupmsg(const_cast<mblk_t *>(m_rtcp)); // const qualifier discarded intentionally.\n\t\ttmp->b_wptr = tmp->b_rptr + rtcp_get_size(m_rtcp);\n\n\t\t// some RTCP packet can be for multiple streams (e.g. BYE)\n\t\tif (RtpSession *session = checkForSession(tmp, false)) {\n\t\t\tauto &pendingMsg = dispatchMap[session];\n\t\t\tif (pendingMsg == nullptr) pendingMsg = tmp;\n\t\t\telse concatb(pendingMsg, tmp);\n\t\t} else {\n\t\t\tconst rtcp_common_header_t *ch = rtcp_get_common_header(tmp);\n\t\t\tortp_warning(\"RtpBundle[%p]: Rctp msg (%d) ssrc=%u does not correspond to any sessions\", this,\n\t\t\t             rtcp_common_header_get_packet_type(ch), getSsrcFromMessage(tmp, false));\n\t\t\tfreemsg(tmp);\n\t\t}\n\t} while ((m_rtcp = rtcp_parser_context_next_packet(&rtcp_parser_ctx)) != nullptr);\n\n\trtcp_parser_context_uninit(&rtcp_parser_ctx);\n\n\tfor (auto &[fst, snd] : dispatchMap) {\n\t\tif (fst == mPrimary) {\n\t\t\tmPrimarymsg = snd;\n\t\t} else {\n\t\t\tRtpSession *session = fst;\n\t\t\tortp_mutex_lock(&session->rtcp.gs.bundleq_lock);\n\t\t\tmsgpullup(snd, static_cast<size_t>(-1));\n\t\t\tputq(&session->rtcp.gs.bundleq, snd);\n\t\t\tortp_mutex_unlock(&session->rtcp.gs.bundleq_lock);\n\t\t}\n\t}\n\n\tfreemsg(m);\n\n\tif (mPrimarymsg) {\n\t\tmsgpullup(mPrimarymsg, static_cast<size_t>(-1));\n\t\treturn mPrimarymsg;\n\t}\n\n\treturn {};\n}\n"
  },
  {
    "path": "src/rtpbundle.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef RTPBUNDLE_H\n#define RTPBUNDLE_H\n\n#include <map>\n#include <mutex>\n#include <optional>\n#include <string>\n\n#include \"ortp/rtpsession.h\"\n\nclass RtpBundleCxx {\n\npublic:\n\tRtpBundleCxx() = default;\n\t~RtpBundleCxx();\n\n\tRtpBundleCxx(const RtpBundleCxx &) = delete;\n\tRtpBundleCxx(RtpBundleCxx &&) = delete;\n\n\tint getMidId() const;\n\tvoid setMidId(int id);\n\tvoid addSession(const std::string &mid, RtpSession *session);\n\tbool findSession(RtpSession *session) const;\n\tbool findMid(const std::string &mid) const;\n\tvoid removeSessions(const std::string &mid);\n\tvoid removeSession(RtpSession *session);\n\tvoid clear();\n\n\tvoid sessionModeUpdated(RtpSession *session, RtpSessionMode previousMode);\n\n\tRtpSession *getPrimarySession() const;\n\tvoid setPrimarySession(RtpSession *session);\n\n\tconst std::string &getSessionMid(RtpSession *session) const;\n\n\t// Dispatch an incoming packet to it's correct destination.\n\t// If a packet is returned then it's destination is the primary session, else it has been dispatched.\n\tstd::optional<mblk_t *> dispatch(bool isRtp, mblk_t *m);\n\n\tRtpSession *checkForSession(const mblk_t *m, bool isRtp, bool isOutgoing = false);\n\nprivate:\n\tstruct Mid {\n\t\tstd::string mid;\n\t\tuint32_t sequenceNumber;\n\t};\n\n\tstruct BundleSession {\n\t\tMid mid;\n\t\tRtpSession *rtpSession = nullptr;\n\t};\n\n\tstatic void checkForSessionSdesCallback(void *, uint32_t, rtcp_sdes_type_t, const char *, uint8_t);\n\tstd::string getMid(const mblk_t *m, bool isRtp);\n\n\tBundleSession *findReferredSession(const uint32_t referredSsrc);\n\n\tstatic void updateBundleSession(BundleSession &session, const std::string &mid, uint32_t sequenceNumber);\n\n\tstd::optional<mblk_t *> dispatchRtpMessage(mblk_t *m);\n\tstd::optional<mblk_t *> dispatchRtcpMessage(mblk_t *m);\n\n\tvoid clearSession(RtpSession *session);\n\n\tRtpSession *mPrimary = nullptr;\n\n\t// Used to remember MID from incoming packets, as not all packets contains a MID.\n\t// This only serves in cases where we are receiving packets for a session that has not been yet added to the bundle\n\t// or being assigned.\n\tstd::map<uint32_t, std::string> mSsrcToMid;\n\n\t// Main map of the bundle, we can directly assign a session to a ssrc (incoming or outgoing) which will speed up the\n\t// transfer to the correct destination.\n\tstd::map<uint32_t, BundleSession> mSsrcToSession;\n\n\t// RCVONLY and SENDRCV sessions do not know their reception's SSRC before receiving any packet. So they are inserted\n\t// into this map. When we receive a packet, we will retrieve all sessions that have the corresponding MID and the\n\t// correct session will be removed from this map and added to the mSsrcToSession for direct access.\n\tstd::multimap<std::string, RtpSession *> mWaitingForAssignment;\n\n\tstd::mutex mAssignmentMutex;\n\n\tstd::string mSdesParseMid;\n\tint mMidId = -1;\n};\n\n#endif /* RTPBUNDLE_H */\n"
  },
  {
    "path": "src/rtpframemarking.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"ortp/logging.h\"\n#include \"ortp/rtp.h\"\n\n/**\n * Add the frame marking header extension.\n * See https://datatracker.ietf.org/doc/html/draft-ietf-avtext-framemarking-13\n * @param packet the RTP packet.\n * @param id the identifier of the frame marking extension.\n * @param marker the frame marker to add.\n **/\nvoid rtp_add_frame_marker(mblk_t *packet, int id, uint8_t marker) {\n\trtp_add_extension_header(packet, id, 1, &marker);\n}\n\n/**\n * Obtain the frame marker through the header extension.\n * See https://datatracker.ietf.org/doc/html/draft-ietf-avtext-framemarking-13\n * @param packet the RTP packet.\n * @param id the identifier of the frame marking extension.\n * @param marker the frame marker to set.\n * @return 1 if the frame marker if present, 0 otherwise.\n **/\nint rtp_get_frame_marker(const mblk_t *packet, int id, uint8_t *marker) {\n\tuint8_t *data;\n\n\tint ret = rtp_get_extension_header(packet, id, &data);\n\tif (ret != -1) {\n\t\t*marker = *data;\n\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}"
  },
  {
    "path": "src/rtpparse.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n\n#include \"congestiondetector.h\"\n#include \"jitterctl.h\"\n#include \"ortp/ortp.h\"\n#include \"rtpsession_priv.h\"\n#include \"utils.h\"\n#include \"videobandwidthestimator.h\"\n\nstatic bool_t queue_packet(queue_t *q, int maxrqsz, mblk_t *mp, rtp_header_t *rtp, int *discarded, int *duplicate) {\n\tmblk_t *tmp;\n\tint header_size;\n\t*discarded = 0;\n\t*duplicate = 0;\n\theader_size = RTP_FIXED_HEADER_SIZE + (4 * rtp->cc);\n\tif ((mp->b_wptr - mp->b_rptr) == header_size) {\n\t\tortp_debug(\"Rtp packet contains no data.\");\n\t\t(*discarded)++;\n\t\tfreemsg(mp);\n\t\treturn FALSE;\n\t}\n\n\t/* and then add the packet to the queue */\n\tif (rtp_putq(q, mp) < 0) {\n\t\t/* It was a duplicate packet */\n\t\t(*duplicate)++;\n\t\treturn FALSE;\n\t}\n\n\t/* make some checks: q size must not exceed RtpStream::max_rq_size */\n\twhile (q->q_mcount > maxrqsz) {\n\t\t/* remove the oldest mblk_t */\n\t\ttmp = getq(q);\n\n\t\tortp_warning(\"rtp_putq: Queue is full. Discarding message with ts=%u\", rtp_get_timestamp(tmp));\n\t\tfreemsg(tmp);\n\t\t(*discarded)++;\n\t}\n\treturn TRUE;\n}\n\nstatic void compute_mean_and_deviation(uint32_t nb, double x, double *olds, double *oldm, double *news, double *newm) {\n\t*newm = *oldm + (x - *oldm) / nb;\n\t*news = *olds + ((x - *oldm) * (x - *newm));\n\t*oldm = *newm;\n\t*olds = *news;\n}\n\nstatic void update_rtcp_xr_stat_summary(RtpSession *session, mblk_t *mp, uint32_t local_str_ts) {\n\tint64_t diff = (int64_t)rtp_get_timestamp(mp) - (int64_t)local_str_ts;\n\n\t/* TTL/HL statistics */\n\tif (session->rtcp_xr_stats.rcv_since_last_stat_summary == 1) {\n\t\tsession->rtcp_xr_stats.min_ttl_or_hl_since_last_stat_summary = 255;\n\t\tsession->rtcp_xr_stats.max_ttl_or_hl_since_last_stat_summary = 0;\n\t\tsession->rtcp_xr_stats.olds_ttl_or_hl_since_last_stat_summary = 0;\n\t\tsession->rtcp_xr_stats.oldm_ttl_or_hl_since_last_stat_summary = mp->ttl_or_hl;\n\t\tsession->rtcp_xr_stats.newm_ttl_or_hl_since_last_stat_summary = mp->ttl_or_hl;\n\t}\n\tcompute_mean_and_deviation(session->rtcp_xr_stats.rcv_since_last_stat_summary, (double)mp->ttl_or_hl,\n\t                           &session->rtcp_xr_stats.olds_ttl_or_hl_since_last_stat_summary,\n\t                           &session->rtcp_xr_stats.oldm_ttl_or_hl_since_last_stat_summary,\n\t                           &session->rtcp_xr_stats.news_ttl_or_hl_since_last_stat_summary,\n\t                           &session->rtcp_xr_stats.newm_ttl_or_hl_since_last_stat_summary);\n\tif (mp->ttl_or_hl < session->rtcp_xr_stats.min_ttl_or_hl_since_last_stat_summary) {\n\t\tsession->rtcp_xr_stats.min_ttl_or_hl_since_last_stat_summary = mp->ttl_or_hl;\n\t}\n\tif (mp->ttl_or_hl > session->rtcp_xr_stats.max_ttl_or_hl_since_last_stat_summary) {\n\t\tsession->rtcp_xr_stats.max_ttl_or_hl_since_last_stat_summary = mp->ttl_or_hl;\n\t}\n\n\t/* Jitter statistics */\n\tif (session->rtcp_xr_stats.rcv_since_last_stat_summary == 1) {\n\t\tsession->rtcp_xr_stats.min_jitter_since_last_stat_summary = 0xFFFFFFFF;\n\t\tsession->rtcp_xr_stats.max_jitter_since_last_stat_summary = 0;\n\t} else {\n\t\tint64_t signed_jitter = diff - session->rtcp_xr_stats.last_jitter_diff_since_last_stat_summary;\n\t\tuint32_t jitter;\n\t\tif (signed_jitter < 0) {\n\t\t\tjitter = (uint32_t)(-signed_jitter);\n\t\t} else {\n\t\t\tjitter = (uint32_t)(signed_jitter);\n\t\t}\n\t\tcompute_mean_and_deviation(session->rtcp_xr_stats.rcv_since_last_stat_summary - 1, (double)jitter,\n\t\t                           &session->rtcp_xr_stats.olds_jitter_since_last_stat_summary,\n\t\t                           &session->rtcp_xr_stats.oldm_jitter_since_last_stat_summary,\n\t\t                           &session->rtcp_xr_stats.news_jitter_since_last_stat_summary,\n\t\t                           &session->rtcp_xr_stats.newm_jitter_since_last_stat_summary);\n\t\tif (jitter < session->rtcp_xr_stats.min_jitter_since_last_stat_summary) {\n\t\t\tsession->rtcp_xr_stats.min_jitter_since_last_stat_summary = jitter;\n\t\t}\n\t\tif (jitter > session->rtcp_xr_stats.max_jitter_since_last_stat_summary) {\n\t\t\tsession->rtcp_xr_stats.max_jitter_since_last_stat_summary = jitter;\n\t\t}\n\t}\n\tsession->rtcp_xr_stats.last_jitter_diff_since_last_stat_summary = diff;\n}\n\nstatic void check_for_seq_number_gap_immediate(RtpSession *session, rtp_header_t *rtp) {\n\tuint16_t pid;\n\tuint16_t i;\n\tuint16_t seq_number = rtp_header_get_seqnumber(rtp);\n\n\t/*don't check anything before first packet delivered*/\n\tif (session->flags & RTP_SESSION_FIRST_PACKET_DELIVERED &&\n\t    RTP_SEQ_IS_STRICTLY_GREATER_THAN(seq_number, session->rtp.rcv_last_seq + 1) &&\n\t    RTP_SEQ_IS_STRICTLY_GREATER_THAN(seq_number, session->rtp.snd_last_nack + 1)) {\n\t\tuint16_t first_missed_seq = session->rtp.rcv_last_seq + 1;\n\t\tuint16_t diff;\n\n\t\tif (first_missed_seq <= session->rtp.snd_last_nack) {\n\t\t\tfirst_missed_seq = session->rtp.snd_last_nack + 1;\n\t\t}\n\n\t\tdiff = seq_number - first_missed_seq;\n\t\tsession->stats.loss_before_nack += diff;\n\n\t\tpid = first_missed_seq;\n\n\t\tfor (i = 0; i <= (diff / 16); i++) {\n\t\t\tuint16_t seq;\n\t\t\tuint16_t blp = 0;\n\t\t\tfor (seq = pid + 1; (seq < seq_number) && ((seq - pid) < 16); seq++) {\n\t\t\t\tblp |= (1 << (seq - pid - 1));\n\t\t\t}\n\t\t\tif (session->rtp.congdetect != NULL && session->rtp.congdetect->state == CongestionStateDetected) {\n\t\t\t\t/*\n\t\t\t\t * Do not send NACK in IMMEDIATE_NACK mode in congestion, because the retransmission by the other party\n\t\t\t\t * of the missing packets will necessarily increase or at least sustain the congestion. Furthermore, due\n\t\t\t\t * to the congestion, the retransmitted packets have very few chance to arrive in time.\n\t\t\t\t */\n\t\t\t\tortp_message(\"Immediate NACK not sent because of congestion.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trtp_session_send_rtcp_fb_generic_nack(session, pid, blp);\n\t\t\tpid = seq;\n\t\t}\n\t}\n\n\tif (RTP_SEQ_IS_STRICTLY_GREATER_THAN(seq_number, session->rtp.snd_last_nack)) {\n\t\t/* We update the last_nack since we received this packet we don't need a nack for it */\n\t\tsession->rtp.snd_last_nack = seq_number;\n\t}\n}\n\nvoid rtp_session_rtp_parse(\n    RtpSession *session, mblk_t *mp, uint32_t local_str_ts, struct sockaddr *addr, socklen_t addrlen) {\n\tint discarded;\n\tint duplicate;\n\trtp_header_t *rtp;\n\tint msgsize;\n\tRtpStream *rtpstream = &session->rtp;\n\trtp_stats_t *stats = &session->stats;\n\tuint16_t seq_number;\n\tuint32_t timestamp, ssrc;\n\n\tmsgsize = (int)(mp->b_wptr - mp->b_rptr);\n\n\tif (msgsize < RTP_FIXED_HEADER_SIZE) {\n\t\tortp_warning(\"Packet too small to be a rtp packet (%i)!\", msgsize);\n\t\tsession->stats.bad++;\n\t\tortp_global_stats.bad++;\n\t\tfreemsg(mp);\n\t\treturn;\n\t}\n\trtp = (rtp_header_t *)mp->b_rptr;\n\tif (rtp->version != 2) {\n\t\t/* try to see if it is a STUN packet */\n\t\tuint16_t stunlen = *((uint16_t *)(mp->b_rptr + sizeof(uint16_t)));\n\t\tstunlen = ntohs(stunlen);\n\t\tif (stunlen + 20 == mp->b_wptr - mp->b_rptr) {\n\t\t\t/* this looks like a stun packet */\n\t\t\trtp_session_update_remote_sock_addr(session, mp, TRUE);\n\t\t\tif (session->eventqs != NULL) {\n\t\t\t\tOrtpEvent *ev = ortp_event_new(ORTP_EVENT_STUN_PACKET_RECEIVED);\n\t\t\t\tOrtpEventData *ed = ortp_event_get_data(ev);\n\t\t\t\ted->packet = mp;\n\t\t\t\tmemcpy(&ed->source_addr, addr, addrlen);\n\t\t\t\ted->source_addrlen = addrlen;\n\t\t\t\ted->info.socket_type = OrtpRTPSocket;\n\t\t\t\trtp_session_dispatch_event(session, ev);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t/* discard in two case: the packet is not stun OR nobody is interested by STUN (no eventqs) */\n\t\tortp_debug(\"Receiving rtp packet with version number %d!=2...discarded\", rtp->version);\n\t\tstats->bad++;\n\t\tortp_global_stats.bad++;\n\t\tfreemsg(mp);\n\t\treturn;\n\t}\n\n\t/* only count non-stun packets. */\n\tortp_global_stats.packet_recv++;\n\tstats->packet_recv++;\n\tortp_global_stats.hw_recv += msgsize;\n\tstats->hw_recv += msgsize;\n\tsession->rtp.hwrcv_since_last_SR++;\n\tsession->rtcp_xr_stats.rcv_since_last_stat_summary++;\n\n\t/* convert all header data from network order to host order */\n\tseq_number = rtp_header_get_seqnumber(rtp);\n\ttimestamp = rtp_header_get_timestamp(rtp);\n\tssrc = rtp_header_get_ssrc(rtp);\n\t/* convert csrc if necessary */\n\tif (rtp->cc * sizeof(uint32_t) > (uint32_t)(msgsize - RTP_FIXED_HEADER_SIZE)) {\n\t\tortp_debug(\"Receiving too short rtp packet.\");\n\t\tstats->bad++;\n\t\tortp_global_stats.bad++;\n\t\tfreemsg(mp);\n\t\treturn;\n\t}\n\n#ifndef PERF\n\t/* Write down the last RTP/RTCP packet reception time. */\n\tbctbx_gettimeofday(&session->last_recv_time, NULL);\n#endif\n\n\t/*the goal of the following code is to lock on an incoming SSRC to avoid\n\treceiving \"mixed streams\"*/\n\tif (session->ssrc_set) {\n\t\t/*the ssrc is set, so we must check it */\n\t\tif (session->rcv.ssrc != ssrc) {\n\t\t\tif (session->inc_ssrc_candidate == ssrc) {\n\t\t\t\tsession->inc_same_ssrc_count++;\n\t\t\t} else {\n\t\t\t\tsession->inc_same_ssrc_count = 0;\n\t\t\t\tsession->inc_ssrc_candidate = ssrc;\n\t\t\t}\n\t\t\tif (session->inc_same_ssrc_count >= session->rtp.ssrc_changed_thres) {\n\t\t\t\t/* store the sender rtp address to do symmetric RTP */\n\t\t\t\trtp_session_update_remote_sock_addr(session, mp, TRUE);\n\t\t\t\tsession->rtp.rcv_last_ts = timestamp;\n\t\t\t\tsession->rcv.ssrc = ssrc;\n\t\t\t\trtp_signal_table_emit(&session->on_ssrc_changed);\n\t\t\t} else {\n\t\t\t\t/*discard the packet*/\n\t\t\t\tortp_debug(\"Receiving packet with unknown ssrc.\");\n\t\t\t\tstats->bad++;\n\t\t\t\tortp_global_stats.bad++;\n\t\t\t\tfreemsg(mp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\t/* The SSRC change must not happen if we still receive\n\t\t\tssrc from the initial source. */\n\t\t\tsession->inc_same_ssrc_count = 0;\n\t\t\trtp_session_update_remote_sock_addr(session, mp, TRUE);\n\t\t}\n\t} else {\n\t\tsession->ssrc_set = TRUE;\n\t\tsession->rcv.ssrc = ssrc;\n\t\trtp_session_update_remote_sock_addr(session, mp, TRUE);\n\t}\n\n\t/* update some statistics */\n\t{\n\t\tpoly32_t *extseq = (poly32_t *)&rtpstream->hwrcv_extseq;\n\t\tif (seq_number > extseq->split.lo) {\n\t\t\textseq->split.lo = seq_number;\n\t\t} else if (seq_number < 200 && extseq->split.lo > ((1 << 16) - 200)) {\n\t\t\t/* this is a check for sequence number looping */\n\t\t\textseq->split.lo = seq_number;\n\t\t\textseq->split.hi++;\n\t\t}\n\n\t\t/* the first sequence number received should be initialized at the beginning\n\t\tor at any resync, so that the first receiver reports contains valid loss rate*/\n\t\tif (!(session->flags & RTP_SESSION_RECV_SEQ_INIT)) {\n\t\t\trtp_session_set_flag(session, RTP_SESSION_RECV_SEQ_INIT);\n\t\t\trtpstream->hwrcv_seq_at_last_SR = seq_number - 1;\n\t\t\tsession->rtcp_xr_stats.rcv_seq_at_last_stat_summary = seq_number - 1;\n\t\t}\n\t\tif (stats->packet_recv == 1) {\n\t\t\tsession->rtcp_xr_stats.first_rcv_seq = extseq->one;\n\t\t}\n\t\tsession->rtcp_xr_stats.last_rcv_seq = extseq->one;\n\t}\n\n\t/* check for possible telephone events */\n\tif (rtp_profile_is_telephone_event(session->snd.profile, rtp->paytype)) {\n\t\tqueue_packet(&session->rtp.tev_rq, session->rtp.jittctl.params.max_packets, mp, rtp, &discarded, &duplicate);\n\t\tstats->discarded += discarded;\n\t\tortp_global_stats.discarded += discarded;\n\t\tstats->packet_dup_recv += duplicate;\n\t\tortp_global_stats.packet_dup_recv += duplicate;\n\t\tsession->rtcp_xr_stats.discarded_count += discarded;\n\t\tsession->rtcp_xr_stats.dup_since_last_stat_summary += duplicate;\n\t\treturn;\n\t}\n\n\t/* check for possible payload type change, in order to update accordingly our clock-rate dependant\n\tparameters */\n\tif (session->hw_recv_pt != rtp->paytype) {\n\t\trtp_session_update_payload_type(session, rtp->paytype);\n\t}\n\n\t/* Drop the packets while the RTP_SESSION_FLUSH flag is set. */\n\tif (session->flags & RTP_SESSION_FLUSH) {\n\t\tfreemsg(mp);\n\t\treturn;\n\t}\n\n\tjitter_control_new_packet(&session->rtp.jittctl, timestamp, local_str_ts);\n\n\tif (session->video_bandwidth_estimator_enabled && session->rtp.video_bw_estimator) {\n\t\tint overhead = ortp_stream_is_ipv6(&session->rtp.gs) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;\n\t\tortp_video_bandwidth_estimator_process_packet(session->rtp.video_bw_estimator, timestamp, &mp->timestamp,\n\t\t                                              msgsize + overhead, rtp->markbit == 1);\n\t}\n\n\tif (session->congestion_detector_enabled && session->rtp.congdetect) {\n\t\tif (ortp_congestion_detector_record(session->rtp.congdetect, timestamp, local_str_ts)) {\n\t\t\tOrtpEvent *ev = ortp_event_new(ORTP_EVENT_CONGESTION_STATE_CHANGED);\n\t\t\tOrtpEventData *ed = ortp_event_get_data(ev);\n\t\t\ted->info.congestion_detected = session->rtp.congdetect->state == CongestionStateDetected;\n\t\t\trtp_session_dispatch_event(session, ev);\n\t\t}\n\t}\n\n\tupdate_rtcp_xr_stat_summary(session, mp, local_str_ts);\n\n\tif (session->flags & RTP_SESSION_FIRST_PACKET_DELIVERED) {\n\t\t/* detect timestamp important jumps in the future, to workaround stupid rtp senders */\n\t\tif (RTP_TIMESTAMP_IS_NEWER_THAN(timestamp, session->rtp.rcv_last_ts + session->rtp.ts_jump)) {\n\t\t\tortp_warning(\"rtp_parse: timestamp jump in the future detected.\");\n\t\t\trtp_signal_table_emit2(&session->on_timestamp_jump, &timestamp);\n\t\t} else if (RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(session->rtp.rcv_last_ts, timestamp) ||\n\t\t           RTP_SEQ_IS_STRICTLY_GREATER_THAN(session->rtp.rcv_last_seq, seq_number)) {\n\t\t\t/* don't queue packets older than the last returned packet to the application, or whose sequence number\n\t\t\t is behind the last packet returned to the application*/\n\t\t\t/* Call timestamp jump in case of\n\t\t\t * large negative Ts jump or if ts is set to 0\n\t\t\t */\n\n\t\t\tif (RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(session->rtp.rcv_last_ts, timestamp + session->rtp.ts_jump)) {\n\t\t\t\tortp_warning(\"rtp_parse: negative timestamp jump detected\");\n\t\t\t\trtp_signal_table_emit2(&session->on_timestamp_jump, &timestamp);\n\t\t\t}\n\t\t\tortp_error(\"rtp_parse: discarding too old packet (seq=%i, ts=%u, last_delivered was seq=%i, ts=%u)\",\n\t\t\t           seq_number, timestamp, (int)session->rtp.rcv_last_seq, session->rtp.rcv_last_ts);\n\t\t\tfreemsg(mp);\n\t\t\tstats->outoftime++;\n\t\t\tortp_global_stats.outoftime++;\n\t\t\tsession->rtcp_xr_stats.discarded_count++;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif ((rtp_session_avpf_enabled(session) == TRUE) &&\n\t    (rtp_session_avpf_feature_enabled(session, ORTP_AVPF_FEATURE_GENERIC_NACK) == TRUE) &&\n\t    (rtp_session_avpf_feature_enabled(session, ORTP_AVPF_FEATURE_IMMEDIATE_NACK) == TRUE)) {\n\t\t/*\n\t\t * If immediate nack is enabled then we check for missing packets here instead of\n\t\t * rtp_session_recvm_with_ts\n\t\t */\n\t\tcheck_for_seq_number_gap_immediate(session, rtp);\n\t}\n\n\tif (queue_packet(&session->rtp.rq, session->rtp.jittctl.params.max_packets, mp, rtp, &discarded, &duplicate))\n\t\tjitter_control_update_size(&session->rtp.jittctl, &session->rtp.rq);\n\tstats->discarded += discarded;\n\tortp_global_stats.discarded += discarded;\n\tstats->packet_dup_recv += duplicate;\n\tortp_global_stats.packet_dup_recv += duplicate;\n\tsession->rtcp_xr_stats.discarded_count += discarded;\n\tsession->rtcp_xr_stats.dup_since_last_stat_summary += duplicate;\n\tif ((discarded == 0) && (duplicate == 0)) {\n\t\tsession->rtcp_xr_stats.rcv_count++;\n\t}\n}\n"
  },
  {
    "path": "src/rtpprofile.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"ortp/ortp.h\"\n#include <bctoolbox/port.h>\n\nint rtp_profile_get_payload_number_from_mime(RtpProfile *profile, const char *mime) {\n\treturn rtp_profile_get_payload_number_from_mime_and_flag(profile, mime, -1);\n}\n\nint rtp_profile_get_payload_number_from_mime_and_flag(RtpProfile *profile, const char *mime, int flag) {\n\tPayloadType *pt;\n\tint i;\n\tfor (i = 0; i < RTP_PROFILE_MAX_PAYLOADS; i++) {\n\t\tpt = rtp_profile_get_payload(profile, i);\n\t\tif (pt != NULL) {\n\t\t\tif (strcasecmp(pt->mime_type, mime) == 0) {\n\t\t\t\tif (flag < 0 || pt->flags & flag) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn -1;\n}\n\nint rtp_profile_find_payload_number(RtpProfile *profile, const char *mime, int rate, int channels) {\n\tint i;\n\tPayloadType *pt;\n\tfor (i = 0; i < RTP_PROFILE_MAX_PAYLOADS; i++) {\n\t\tpt = rtp_profile_get_payload(profile, i);\n\t\tif (pt != NULL) {\n\t\t\tif (strcasecmp(pt->mime_type, mime) == 0 && pt->clock_rate == rate &&\n\t\t\t    (pt->channels == channels || channels <= 0 || pt->channels <= 0)) {\n\t\t\t\t/*we don't look at channels if it is undefined\n\t\t\t\tie a negative or zero value*/\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t}\n\treturn -1;\n}\n\nint rtp_profile_get_payload_number_from_rtpmap(RtpProfile *profile, const char *rtpmap) {\n\tint clock_rate, channels, ret;\n\tchar *subtype = ortp_strdup(rtpmap);\n\tchar *rate_str = NULL;\n\tchar *chan_str = NULL;\n\n\t/* find the slash after the subtype */\n\trate_str = strchr(subtype, '/');\n\tif (rate_str && strlen(rate_str) > 1) {\n\t\t*rate_str = 0;\n\t\trate_str++;\n\n\t\t/* Look for another slash */\n\t\tchan_str = strchr(rate_str, '/');\n\t\tif (chan_str && strlen(chan_str) > 1) {\n\t\t\t*chan_str = 0;\n\t\t\tchan_str++;\n\t\t} else {\n\t\t\tchan_str = NULL;\n\t\t}\n\t} else {\n\t\trate_str = NULL;\n\t}\n\n\t// Use default clock rate if none given\n\tif (rate_str) clock_rate = atoi(rate_str);\n\telse clock_rate = 8000;\n\n\t// Use default number of channels if none given\n\tif (chan_str) channels = atoi(chan_str);\n\telse channels = -1;\n\n\t// printf(\"Searching for payload %s at freq %i with %i channels\\n\",subtype,clock_rate,ch1annels);\n\tret = rtp_profile_find_payload_number(profile, subtype, clock_rate, channels);\n\tortp_free(subtype);\n\treturn ret;\n}\n\nPayloadType *rtp_profile_find_payload(RtpProfile *prof, const char *mime, int rate, int channels) {\n\tint i;\n\ti = rtp_profile_find_payload_number(prof, mime, rate, channels);\n\tif (i >= 0) return rtp_profile_get_payload(prof, i);\n\treturn NULL;\n}\n\nPayloadType *rtp_profile_get_payload_from_mime(RtpProfile *profile, const char *mime) {\n\tint pt;\n\tpt = rtp_profile_get_payload_number_from_mime(profile, mime);\n\tif (pt == -1) return NULL;\n\telse return rtp_profile_get_payload(profile, pt);\n}\n\nPayloadType *rtp_profile_get_payload_from_rtpmap(RtpProfile *profile, const char *rtpmap) {\n\tint pt = rtp_profile_get_payload_number_from_rtpmap(profile, rtpmap);\n\tif (pt == -1) return NULL;\n\telse return rtp_profile_get_payload(profile, pt);\n}\n\nint rtp_profile_move_payload(RtpProfile *prof, int oldpos, int newpos) {\n\tif (oldpos < 0 || oldpos >= RTP_PROFILE_MAX_PAYLOADS) {\n\t\tortp_error(\"Bad old pos index %i\", oldpos);\n\t\treturn -1;\n\t}\n\tif (newpos < 0 || newpos >= RTP_PROFILE_MAX_PAYLOADS) {\n\t\tortp_error(\"Bad new pos index %i\", newpos);\n\t\treturn -1;\n\t}\n\tprof->payload[newpos] = prof->payload[oldpos];\n\tprof->payload[oldpos] = NULL;\n\treturn 0;\n}\n\nRtpProfile *rtp_profile_new(const char *name) {\n\tRtpProfile *prof = (RtpProfile *)ortp_new0(RtpProfile, 1);\n\trtp_profile_set_name(prof, name);\n\treturn prof;\n}\n\n/**\n *\tAssign payload type number index to payload type desribed in pt for the RTP profile profile.\n * @param profile a RTP profile\n * @param idx the payload type number\n * @param pt the payload type description\n *\n **/\nvoid rtp_profile_set_payload(RtpProfile *profile, int idx, PayloadType *pt) {\n\tif (idx < 0 || idx >= RTP_PROFILE_MAX_PAYLOADS) {\n\t\tortp_error(\"Bad index %i\", idx);\n\t\treturn;\n\t}\n\tprofile->payload[idx] = pt;\n}\n\n/**\n * Initialize the profile to the empty profile (all payload type are unassigned).\n *@param profile a RTP profile\n *\n **/\nvoid rtp_profile_clear_all(RtpProfile *profile) {\n\tint i;\n\tfor (i = 0; i < RTP_PROFILE_MAX_PAYLOADS; i++) {\n\t\tprofile->payload[i] = 0;\n\t}\n}\n\n/**\n * Set a name to the rtp profile. (This is not required)\n * @param profile a rtp profile object\n * @param name a string\n *\n **/\nvoid rtp_profile_set_name(RtpProfile *profile, const char *name) {\n\tif (profile->name != NULL) ortp_free(profile->name);\n\tprofile->name = ortp_strdup(name);\n}\n\n/* ! payload are not cloned*/\nRtpProfile *rtp_profile_clone(RtpProfile *prof) {\n\tint i;\n\tPayloadType *pt;\n\tRtpProfile *newprof = rtp_profile_new(prof->name);\n\tfor (i = 0; i < RTP_PROFILE_MAX_PAYLOADS; i++) {\n\t\tpt = rtp_profile_get_payload(prof, i);\n\t\tif (pt != NULL) {\n\t\t\trtp_profile_set_payload(newprof, i, pt);\n\t\t}\n\t}\n\treturn newprof;\n}\n\n/*clone a profile and its payloads */\nRtpProfile *rtp_profile_clone_full(RtpProfile *prof) {\n\tint i;\n\tPayloadType *pt;\n\tRtpProfile *newprof = rtp_profile_new(prof->name);\n\tfor (i = 0; i < RTP_PROFILE_MAX_PAYLOADS; i++) {\n\t\tpt = rtp_profile_get_payload(prof, i);\n\t\tif (pt != NULL) {\n\t\t\trtp_profile_set_payload(newprof, i, payload_type_clone(pt));\n\t\t}\n\t}\n\treturn newprof;\n}\n\nvoid rtp_profile_destroy(RtpProfile *prof) {\n\tint i;\n\tPayloadType *payload;\n\tif (prof->name) {\n\t\tortp_free(prof->name);\n\t\tprof->name = NULL;\n\t}\n\tfor (i = 0; i < RTP_PROFILE_MAX_PAYLOADS; i++) {\n\t\tpayload = rtp_profile_get_payload(prof, i);\n\t\tif (payload != NULL && (payload->flags & PAYLOAD_TYPE_ALLOCATED)) payload_type_destroy(payload);\n\t}\n\tortp_free(prof);\n}\n"
  },
  {
    "path": "src/rtpsession.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <bctoolbox/defs.h>\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n\n#include \"audiobandwidthestimator.h\"\n#include \"congestiondetector.h\"\n#include \"jitterctl.h\"\n#include \"ortp/ortp.h\"\n#include \"ortp/rtcp.h\"\n#include \"ortp/telephonyevents.h\"\n#include \"rtpsession_priv.h\"\n#include \"scheduler.h\"\n#include \"utils.h\"\n#include \"videobandwidthestimator.h\"\n\n#if (_WIN32_WINNT >= 0x0600)\n#include <delayimp.h>\n#undef ExternC /* avoid redefinition... */\n#ifdef ORTP_WINDOWS_DESKTOP\n#include <QOS2.h>\n#include <VersionHelpers.h>\n#endif\n#endif\n\n#ifdef HAVE_SYS_UIO_H\n#include <sys/uio.h>\n#ifdef HAVE_RECVMSG\n#define USE_RECVMSG 1\n#endif\n#ifdef HAVE_SENDMSG\n#define USE_SENDMSG 1\n#endif\n#endif\n\nstatic void ortp_stream_init(OrtpStream *os);\n\n/**\n * #_RtpTransport object which can handle multiples security protocols. You can for instance use this object\n * to use both sRTP and tunnel transporter. mblk_t messages received and sent from the endpoint\n * will pass through the list of modifiers given. First modifier in list will be first to modify the message\n * in send mode and last in receive mode.\n * @param[in] endpoint #_RtpTransport object in charge of sending/receiving packets. If NULL, it will use standards\n *sendto and recvfrom functions.\n * @param[in] modifiers_count number of #_RtpTransport object given in the variadic list. Must be 0 if none are given.\n * @returns #_RtpTransport object that will be generated or NULL.\n **/\nRtpTransport *meta_rtp_transport_new(RtpTransport *endpoint, unsigned modifiers_count, ...);\nRtpTransport *meta_rtcp_transport_new(RtpTransport *endpoint, unsigned modifiers_count, ...);\nvoid meta_rtp_transport_link(RtpTransport *rtp, RtpTransport *rtcp);\n\n/* this function initialize all session parameter's that depend on the payload type */\nstatic void payload_type_changed(RtpSession *session, PayloadType *pt) {\n\tjitter_control_set_payload(&session->rtp.jittctl, pt);\n\trtp_session_set_time_jump_limit(session, session->rtp.time_jump);\n\tif (pt->type == PAYLOAD_VIDEO) {\n\t\tsession->permissive = TRUE;\n\t\tortp_message(\"Using permissive algorithm\");\n\t} else session->permissive = FALSE;\n}\n\nvoid wait_point_init(WaitPoint *wp) {\n\tortp_mutex_init(&wp->lock, NULL);\n\tortp_cond_init(&wp->cond, NULL);\n\twp->time = 0;\n\twp->wakeup = FALSE;\n}\nvoid wait_point_uninit(WaitPoint *wp) {\n\tortp_cond_destroy(&wp->cond);\n\tortp_mutex_destroy(&wp->lock);\n}\n\n#define wait_point_lock(wp) ortp_mutex_lock(&(wp)->lock)\n#define wait_point_unlock(wp) ortp_mutex_unlock(&(wp)->lock)\n\nvoid wait_point_wakeup_at(WaitPoint *wp, uint32_t t, bool_t dosleep) {\n\twp->time = t;\n\twp->wakeup = TRUE;\n\tif (dosleep) ortp_cond_wait(&wp->cond, &wp->lock);\n}\n\nbool_t wait_point_check(WaitPoint *wp, uint32_t t) {\n\tbool_t ok = FALSE;\n\n\tif (wp->wakeup) {\n\t\tif (TIME_IS_NEWER_THAN(t, wp->time)) {\n\t\t\twp->wakeup = FALSE;\n\t\t\tok = TRUE;\n\t\t}\n\t}\n\treturn ok;\n}\n#define wait_point_wakeup(wp) ortp_cond_signal(&(wp)->cond);\n\nextern void rtp_parse(RtpSession *session, mblk_t *mp, uint32_t local_str_ts, struct sockaddr *addr, socklen_t addrlen);\n\n/* put an rtp packet in queue. It is called by rtp_parse()\n   A return value of -1 means the packet was a duplicate, 0 means the packet was ok */\nint rtp_putq(queue_t *q, mblk_t *mp) {\n\tmblk_t *tmp;\n\tuint16_t seq_number = rtp_get_seqnumber(mp);\n\n\t/* insert message block by increasing time stamp order : the last (at the bottom)\n\t    message of the queue is the newest*/\n\tortp_debug(\"rtp_putq(): Enqueuing packet with ts=%u and seq=%i\", rtp_get_timestamp(mp), seq_number);\n\n\tif (qempty(q)) {\n\t\tputq(q, mp);\n\t\treturn 0;\n\t}\n\ttmp = qlast(q);\n\t/* we look at the queue from bottom to top, because enqueued packets have a better chance\n\tto be enqueued at the bottom, since there are surely newer */\n\twhile (!qend(q, tmp)) {\n\t\tuint16_t tmp_seq_number = rtp_get_seqnumber(tmp);\n\t\tortp_debug(\"rtp_putq(): Seeing packet with seq=%i\", tmp_seq_number);\n\n\t\tif (seq_number == tmp_seq_number) {\n\t\t\t/* this is a duplicated packet. Don't queue it */\n\t\t\tortp_debug(\"rtp_putq: duplicated message.\");\n\t\t\tfreemsg(mp);\n\t\t\treturn -1;\n\t\t} else if (RTP_SEQ_IS_STRICTLY_GREATER_THAN(seq_number, tmp_seq_number)) {\n\t\t\tortp_debug(\"rtp_putq: seq is strictly greater %u, %u\", seq_number, tmp_seq_number);\n\t\t\tinsq(q, tmp->b_next, mp);\n\t\t\treturn 0;\n\t\t}\n\t\ttmp = tmp->b_prev;\n\t}\n\t/* this packet is the oldest, it has to be\n\tplaced on top of the queue */\n\tinsq(q, qfirst(q), mp);\n\treturn 0;\n}\n\nmblk_t *rtp_peekq(queue_t *q, uint32_t timestamp, int *rejected) {\n\tmblk_t *tmp, *ret = NULL, *old = NULL;\n\tuint32_t ts_found = 0;\n\n\t*rejected = 0;\n\tortp_debug(\"rtp_getq(): Timestamp %u wanted.\", timestamp);\n\tif (qempty(q)) {\n\t\t/*ortp_debug(\"rtp_getq: q is empty.\");*/\n\t\treturn NULL;\n\t}\n\t/* return the packet with ts just equal or older than the asked timestamp */\n\t/* packets with older timestamps are discarded */\n\twhile ((tmp = qfirst(q)) != NULL) {\n\t\tuint32_t tmp_timestamp = rtp_get_timestamp(tmp);\n\t\tortp_debug(\"rtp_getq: Seeing packet with ts=%u\", tmp_timestamp);\n\n\t\tif (RTP_TIMESTAMP_IS_NEWER_THAN(timestamp, tmp_timestamp)) {\n\t\t\tif (ret != NULL && tmp_timestamp == ts_found) {\n\t\t\t\t/* we've found two packets with same timestamp. return the first one */\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (old != NULL) {\n\t\t\t\tortp_debug(\"rtp_getq: discarding too old packet with ts=%u\", ts_found);\n\t\t\t\t(*rejected)++;\n\t\t\t\tfreemsg(old);\n\t\t\t}\n\t\t\tret = peekq(q); /* dequeue the packet, since it has an interesting timestamp*/\n\t\t\tts_found = tmp_timestamp;\n\t\t\tortp_debug(\"rtp_getq: Found packet with ts=%u\", tmp_timestamp);\n\n\t\t\told = ret;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn ret;\n}\n\nmblk_t *rtp_peekq_permissive(queue_t *q, uint32_t timestamp, int *rejected) {\n\tmblk_t *tmp, *ret = NULL;\n\tuint32_t tmp_timestamp;\n\n\t*rejected = 0;\n\tortp_debug(\"rtp_getq_permissive(): Timestamp %u wanted.\", timestamp);\n\n\tif (qempty(q)) {\n\t\t/*ortp_debug(\"rtp_getq: q is empty.\");*/\n\t\treturn NULL;\n\t}\n\t/* return the packet with the older timestamp (provided that it is older than\n\tthe asked timestamp) */\n\ttmp = qfirst(q);\n\ttmp_timestamp = rtp_get_timestamp(tmp);\n\tortp_debug(\"rtp_getq_permissive: Seeing packet with ts=%u, seq=%u\", tmp_timestamp, rtp_get_seqnumber(tmp));\n\tif (RTP_TIMESTAMP_IS_NEWER_THAN(timestamp, tmp_timestamp)) {\n\t\tret = peekq(q); /* dequeue the packet, since it has an interesting timestamp*/\n\t\tortp_debug(\"rtp_getq_permissive: Found packet with ts=%u\", tmp_timestamp);\n\t}\n\treturn ret;\n}\n\nvoid rtp_session_init(RtpSession *session, RtpSessionMode mode) {\n\tJBParameters jbp;\n\tif (session == NULL) {\n\t\tortp_debug(\"rtp_session_init: Invalid paramter (session=NULL)\");\n\t\treturn;\n\t}\n\tmemset(session, 0, sizeof(RtpSession));\n\tortp_mutex_init(&session->main_mutex, NULL);\n\tsession->mode = (RtpSessionMode)mode;\n\tif ((mode == RTP_SESSION_RECVONLY) || (mode == RTP_SESSION_SENDRECV)) {\n\t\trtp_session_set_flag(session, RTP_SESSION_RECV_SYNC);\n\t\trtp_session_set_flag(session, RTP_SESSION_RECV_NOT_STARTED);\n\t}\n\n\trtp_session_set_flag(session, RTP_SESSION_SEND_NOT_STARTED);\n\tsession->snd.ssrc = bctbx_random();\n\t/* set default source description */\n\trtp_session_set_source_description(session, \"unknown@unknown\", NULL, NULL, NULL, NULL, \"oRTP-\" ORTP_VERSION, NULL);\n\trtp_session_set_profile(session, &av_profile); /*the default profile to work with */\n\tsession->rtp.gs.socket = -1;\n\tsession->rtcp.gs.socket = -1;\n#ifndef _WIN32\n\tsession->rtp.snd_socket_size = 0; /*use OS default value unless on windows where they are definitely too short*/\n\tsession->rtp.rcv_socket_size = 0;\n#else\n\tsession->rtp.snd_socket_size = session->rtp.rcv_socket_size = 65536;\n#endif\n\tsession->rtp.ssrc_changed_thres = 50;\n\tsession->dscp = RTP_DEFAULT_DSCP;\n\tsession->multicast_ttl = RTP_DEFAULT_MULTICAST_TTL;\n\tsession->multicast_loopback = RTP_DEFAULT_MULTICAST_LOOPBACK;\n\tqinit(&session->rtp.rq);\n\tqinit(&session->rtp.tev_rq);\n\tqinit(&session->rtp.winrq);\n\tqinit(&session->contributing_sources);\n\tsession->eventqs = NULL;\n\n\t/* Initialize RTCP send algorithm */\n\tsession->target_upload_bandwidth = 80000; /* 80kbits/s to have 4kbits/s dedicated to RTCP if\n\t                                             rtp_session_set_target_upload_bandwidth() is not called. */\n\tsession->max_target_upload_bandwidth = 0; /* this one is set only using rtp_session_set_target_upload_bandwidth() */\n\tsession->rtcp.send_algo.initial = TRUE;\n\tsession->rtcp.send_algo.allow_early = TRUE;\n\n\t/* init signal tables */\n\trtp_signal_table_init(&session->on_ssrc_changed, session, \"ssrc_changed\");\n\trtp_signal_table_init(&session->on_payload_type_changed, session, \"payload_type_changed\");\n\trtp_signal_table_init(&session->on_telephone_event, session, \"telephone-event\");\n\trtp_signal_table_init(&session->on_telephone_event_packet, session, \"telephone-event_packet\");\n\trtp_signal_table_init(&session->on_timestamp_jump, session, \"timestamp_jump\");\n\trtp_signal_table_init(&session->on_network_error, session, \"network_error\");\n\trtp_signal_table_init(&session->on_rtcp_bye, session, \"rtcp_bye\");\n\trtp_signal_table_init(&session->on_new_incoming_ssrc_in_bundle, session, \"new_incoming_ssrc_found_in_bundle\");\n\trtp_signal_table_init(&session->on_new_outgoing_ssrc_in_bundle, session, \"new_outgoing_ssrc_found_in_bundle\");\n\twait_point_init(&session->snd.wp);\n\twait_point_init(&session->rcv.wp);\n\t/*defaults send payload type to 0 (pcmu)*/\n\trtp_session_set_send_payload_type(session, 0);\n\t/*sets supposed recv payload type to undefined */\n\trtp_session_set_recv_payload_type(session, -1);\n\n\trtp_session_enable_jitter_buffer(session, TRUE);\n\tjb_parameters_init(&jbp);\n\trtp_session_set_jitter_buffer_params(session, &jbp);\n\trtp_session_set_time_jump_limit(session, 5000);\n\trtp_session_enable_rtcp(session, TRUE);\n\trtp_session_set_rtcp_report_interval(session, RTCP_DEFAULT_REPORT_INTERVAL);\n\tsession->recv_buf_size = UDP_MAX_SIZE;\n\tsession->symmetric_rtp = FALSE;\n\tsession->permissive = FALSE;\n\tsession->reuseaddr = TRUE;\n\tortp_stream_init(&session->rtp.gs);\n\tortp_stream_init(&session->rtcp.gs);\n\t/*set default rtptransport*/\n\n\t{\n\t\tRtpTransport *rtp_tr = meta_rtp_transport_new(NULL, 0);\n\t\tRtpTransport *rtcp_tr = meta_rtcp_transport_new(NULL, 0);\n\t\tmeta_rtp_transport_link(rtp_tr, rtcp_tr);\n\t\trtp_session_set_transports(session, rtp_tr, rtcp_tr);\n\t}\n\tsession->tev_send_pt = -1; /*check in rtp profile when needed*/\n\n\tsession->rtp.gs.recv_bw_estimator = ortp_bandwidth_measurer_short_term_new();\n\tsession->rtcp.gs.recv_bw_estimator = ortp_bandwidth_measurer_short_term_new();\n\tsession->rtp.gs.recv_average_bw_estimator = ortp_bandwidth_measurer_long_term_new();\n\tsession->rtcp.gs.recv_average_bw_estimator = ortp_bandwidth_measurer_long_term_new();\n\n\tsession->rtp.gs.send_bw_estimator = ortp_bandwidth_measurer_short_term_new();\n\tsession->rtcp.gs.send_bw_estimator = ortp_bandwidth_measurer_short_term_new();\n\tsession->rtp.gs.send_average_bw_estimator = ortp_bandwidth_measurer_long_term_new();\n\tsession->rtcp.gs.send_average_bw_estimator = ortp_bandwidth_measurer_long_term_new();\n\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\tsession->rtp.is_win_thread_running = FALSE;\n\tortp_mutex_init(&session->rtp.winthread_lock, NULL);\n\tortp_mutex_init(&session->rtp.winrq_lock, NULL);\n#endif\n\n\tsession->bundle = NULL;\n\tsession->is_primary = FALSE;\n\tsession->transfer_mode = FALSE;\n\n\tsession->rtp.gs.remote_address_adaptation = TRUE;\n\tsession->rtcp.gs.remote_address_adaptation = TRUE;\n\tsession->rtp.gs.rem_addr_previously_set_len = 0;\n\tsession->rtcp.gs.rem_addr_previously_set_len = 0;\n}\n\nvoid rtp_session_set_mode(RtpSession *session, RtpSessionMode mode) {\n\tconst RtpSessionMode previous_mode = session->mode;\n\n\tsession->mode = mode;\n\n\t// If we have a bundle, we warn it about the change\n\tif (session->bundle) {\n\t\trtp_bundle_session_mode_updated(session->bundle, session, previous_mode);\n\t}\n}\n\nvoid rtp_session_enable_congestion_detection(RtpSession *session, bool_t enabled) {\n\tif (enabled) {\n\t\tif (session->rtp.jittctl.params.buffer_algorithm != OrtpJitterBufferRecursiveLeastSquare) {\n\t\t\tortp_error(\"rtp_session_enable_congestion_detection(): cannot use congestion control without RLS jitter \"\n\t\t\t           \"buffer algorithm\");\n\t\t\treturn;\n\t\t}\n\t\tif (!session->rtp.congdetect) {\n\t\t\tsession->rtp.congdetect = ortp_congestion_detector_new(session);\n\t\t} else {\n\t\t\tif (!session->congestion_detector_enabled) ortp_congestion_detector_reset(session->rtp.congdetect);\n\t\t}\n\t}\n\tsession->congestion_detector_enabled = enabled;\n}\n\nvoid rtp_session_reset_video_bandwidth_estimator(RtpSession *session) {\n\tortp_video_bandwidth_estimator_reset(session->rtp.video_bw_estimator);\n}\n\nvoid rtp_session_enable_video_bandwidth_estimator(RtpSession *session,\n                                                  const OrtpVideoBandwidthEstimatorParams *params) {\n\tif (params->enabled) {\n\t\tif (!session->rtp.video_bw_estimator) {\n\t\t\tsession->rtp.video_bw_estimator = ortp_video_bandwidth_estimator_new(session);\n\t\t}\n\t\tif (params->packet_count_min > 0)\n\t\t\tortp_video_bandwidth_estimator_set_packets_count_min(session->rtp.video_bw_estimator,\n\t\t\t                                                     params->packet_count_min);\n\t\tif (params->min_required_measurements > 0)\n\t\t\tortp_video_bandwidth_estimator_set_min_measurements_count(session->rtp.video_bw_estimator,\n\t\t\t                                                          params->min_required_measurements);\n\t\tif (params->trust_percentage > 0)\n\t\t\tortp_video_bandwidth_estimator_set_trust(session->rtp.video_bw_estimator, params->trust_percentage);\n\t\tif (!session->video_bandwidth_estimator_enabled)\n\t\t\tortp_video_bandwidth_estimator_reset(session->rtp.video_bw_estimator);\n\t}\n\tsession->video_bandwidth_estimator_enabled = params->enabled;\n}\n\nvoid rtp_session_enable_audio_bandwidth_estimator(RtpSession *session,\n                                                  const OrtpAudioBandwidthEstimatorParams *params) {\n\tif (params->enabled) {\n\t\tif (!session->rtp.audio_bw_estimator) {\n\t\t\tsession->rtp.audio_bw_estimator = ortp_audio_bandwidth_estimator_new(session);\n\t\t}\n\t\tif (params->packets_history_size > 0)\n\t\t\tsession->rtp.audio_bw_estimator->packets_history_size = params->packets_history_size;\n\t\tif (params->trust_percentage > 0) session->rtp.audio_bw_estimator->trust_percentage = params->trust_percentage;\n\t\tif (params->duplicated_packet_rate > 0)\n\t\t\tsession->rtp.audio_bw_estimator->duplicated_packet_rate = params->duplicated_packet_rate;\n\t\tif (!session->audio_bandwidth_estimator_enabled)\n\t\t\tortp_audio_bandwidth_estimator_reset(session->rtp.audio_bw_estimator);\n\t}\n\tsession->audio_bandwidth_estimator_enabled = params->enabled;\n}\n\nvoid jb_parameters_init(JBParameters *jbp) {\n\t/* configure jitter buffer with working default parameters */\n\tjbp->min_size = RTP_DEFAULT_JITTER_TIME;\n\tjbp->nom_size = RTP_DEFAULT_JITTER_TIME;\n\tjbp->max_size = 500;\n\tjbp->max_packets = 200; /* maximum number of packet allowed to be queued */\n\tjbp->adaptive = TRUE;\n\tjbp->enabled = TRUE;\n\tjbp->buffer_algorithm = OrtpJitterBufferRecursiveLeastSquare;\n\tjbp->refresh_ms = 5000;\n\tjbp->ramp_threshold = 70;\n\tjbp->ramp_step_ms = 20;\n\tjbp->ramp_refresh_ms = 5000;\n}\n\n/**\n * Creates a new rtp session.\n * If the session is able to send data (RTP_SESSION_SENDONLY or\n * RTP_SESSION_SENDRECV), then a random SSRC number is choosed for\n * the outgoing stream.\n * @param mode One of the RtpSessionMode flags.\n *\n * @return the newly created rtp session.\n **/\nRtpSession *rtp_session_new(RtpSessionMode mode) {\n\tRtpSession *session;\n\tsession = (RtpSession *)ortp_malloc(sizeof(RtpSession));\n\tif (session == NULL) {\n\t\tortp_error(\"rtp_session_new: Memory allocation failed\");\n\t\treturn NULL;\n\t}\n\trtp_session_init(session, mode);\n\treturn session;\n}\n\n/**\n * Sets the scheduling mode of the rtp session. If \\a yesno is TRUE, the rtp session is in\n *\tthe scheduled mode, that means that you can use session_set_select() to block until it's time\n *\tto receive or send on this session according to the timestamp passed to the respective functions.\n *  You can also use blocking mode (see rtp_session_set_blocking_mode() ), to simply block within\n *\tthe receive and send functions.\n *\tIf \\a yesno is FALSE, the ortp scheduler will not manage those sessions, meaning that blocking mode\n *  and the use of session_set_select() for this session are disabled.\n *@param session a rtp session.\n *@param yesno \ta boolean to indicate the scheduling mode.\n *\n *\n **/\nvoid rtp_session_set_scheduling_mode(RtpSession *session, int yesno) {\n\tif (yesno) {\n\t\tRtpScheduler *sched;\n\t\tsched = ortp_get_scheduler();\n\t\tif (sched != NULL) {\n\t\t\trtp_session_set_flag(session, RTP_SESSION_SCHEDULED);\n\t\t\tsession->sched = sched;\n\t\t\trtp_scheduler_add_session(sched, session);\n\t\t} else\n\t\t\tortp_warning(\"rtp_session_set_scheduling_mode: Cannot use scheduled mode because the \"\n\t\t\t             \"scheduler is not started. Use ortp_scheduler_init() before.\");\n\t} else rtp_session_unset_flag(session, RTP_SESSION_SCHEDULED);\n}\n\n/**\n *\tThis function implicitely enables the scheduling mode if yesno is TRUE.\n *\trtp_session_set_blocking_mode() defines the behaviour of the rtp_session_recv_with_ts() and\n *\trtp_session_send_with_ts() functions. If \\a yesno is TRUE, rtp_session_recv_with_ts()\n *\twill block until it is time for the packet to be received, according to the timestamp\n *\tpassed to the function. After this time, the function returns.\n *\tFor rtp_session_send_with_ts(), it will block until it is time for the packet to be sent.\n *\tIf \\a yesno is FALSE, then the two functions will return immediately.\n *\n *  @param session a rtp session\n *  @param yesno a boolean\n **/\nvoid rtp_session_set_blocking_mode(RtpSession *session, int yesno) {\n\tif (yesno) {\n\t\trtp_session_set_scheduling_mode(session, TRUE);\n\t\trtp_session_set_flag(session, RTP_SESSION_BLOCKING_MODE);\n\t} else rtp_session_unset_flag(session, RTP_SESSION_BLOCKING_MODE);\n}\n\n/**\n *\tSet the RTP profile to be used for the session. By default, all session are created by\n *\trtp_session_new() are initialized with the AV profile, as defined in RFC 3551. The application\n *\tcan set any other profile instead using that function.\n *\n * @param session a rtp session\n * @param profile a rtp profile\n **/\n\nvoid rtp_session_set_profile(RtpSession *session, RtpProfile *profile) {\n\tsession->snd.profile = profile;\n\tsession->rcv.profile = profile;\n\trtp_session_telephone_events_supported(session);\n}\n\n/**\n *\tBy default oRTP automatically sends RTCP SR or RR packets. If\n *\tyesno is set to FALSE, the RTCP sending of packet is disabled.\n *\tThis functionality might be needed for some equipments that do not\n *\tsupport RTCP, leading to a traffic of ICMP errors on the network.\n *\tIt can also be used to save bandwidth despite the RTCP bandwidth is\n *\tactually and usually very very low.\n **/\nvoid rtp_session_enable_rtcp(RtpSession *session, bool_t yesno) {\n\tsession->rtcp.enabled = yesno;\n}\n\nbool_t rtp_session_rtcp_enabled(const RtpSession *session) {\n\treturn session->rtcp.enabled;\n}\n\n/**\n * Sets the default interval in milliseconds for RTCP reports emitted by the session\n *\n **/\nvoid rtp_session_set_rtcp_report_interval(RtpSession *session, int value_ms) {\n\tif (value_ms <= 0) session->rtcp.send_algo.T_rr_interval = 0;\n\telse session->rtcp.send_algo.T_rr_interval = (uint32_t)value_ms;\n}\n\nvoid rtp_session_set_target_upload_bandwidth(RtpSession *session, int target_bandwidth) {\n\tortp_message(\"RtpSession: target upload bandwidth set to %i\", target_bandwidth);\n\tsession->target_upload_bandwidth = target_bandwidth;\n\tif (target_bandwidth > session->max_target_upload_bandwidth) {\n\t\tsession->max_target_upload_bandwidth = target_bandwidth;\n\t}\n}\nint rtp_session_get_target_upload_bandwidth(RtpSession *session) {\n\treturn session->target_upload_bandwidth;\n}\n\n/**\n *\tSet the RTP profile to be used for the sending by this session. By default, all session are created by\n *\trtp_session_new() are initialized with the AV profile, as defined in RFC 3551. The application\n *\tcan set any other profile instead using that function.\n * @param session a rtp session\n * @param profile a rtp profile\n *\n **/\n\nvoid rtp_session_set_send_profile(RtpSession *session, RtpProfile *profile) {\n\tsession->snd.profile = profile;\n\trtp_session_send_telephone_events_supported(session);\n}\n\n/**\n *\tSet the RTP profile to be used for the receiveing by this session. By default, all session are created by\n *\trtp_session_new() are initialized with the AV profile, as defined in RFC 3551. The application\n *\tcan set any other profile instead using that function.\n *\n * @param session a rtp session\n * @param profile a rtp profile\n **/\n\nvoid rtp_session_set_recv_profile(RtpSession *session, RtpProfile *profile) {\n\tsession->rcv.profile = profile;\n\trtp_session_recv_telephone_events_supported(session);\n}\n\n/**\n *@param session a rtp session\n *\n *\tDEPRECATED! Returns current send profile.\n *\tUse rtp_session_get_send_profile() or rtp_session_get_recv_profile()\n *\n **/\nRtpProfile *rtp_session_get_profile(RtpSession *session) {\n\treturn session->snd.profile;\n}\n\n/**\n *@param session a rtp session\n *\n *\tReturns current send profile.\n *\n **/\nRtpProfile *rtp_session_get_send_profile(RtpSession *session) {\n\treturn session->snd.profile;\n}\n\n/**\n *@param session a rtp session\n *\n *\tReturns current receive profile.\n *\n **/\nRtpProfile *rtp_session_get_recv_profile(RtpSession *session) {\n\treturn session->rcv.profile;\n}\n\nvoid rtp_session_set_send_ts_offset(RtpSession *s, uint32_t offset) {\n\ts->send_ts_offset = offset;\n}\n\nuint32_t rtp_session_get_send_ts_offset(RtpSession *s) {\n\treturn s->send_ts_offset;\n}\n\n/**\n *\tThe default value is UDP_MAX_SIZE bytes, a value which is working for mostly everyone.\n *\tHowever if your application can make assumption on the sizes of received packet,\n *\tit can be interesting to set it to a lower value in order to save memory.\n *\n * @param session a rtp session\n * @param bufsize max size in bytes for receiving packets\n **/\nvoid rtp_session_set_recv_buf_size(RtpSession *session, int bufsize) {\n\tsession->recv_buf_size = bufsize;\n}\n\n/**\n *\tSet kernel send maximum buffer size for the rtp socket.\n *\tA value of zero defaults to the operating system default.\n **/\nvoid rtp_session_set_rtp_socket_send_buffer_size(RtpSession *session, unsigned int size) {\n\tsession->rtp.snd_socket_size = size;\n\t_rtp_session_apply_socket_sizes(session);\n}\n\n/**\n *\tSet kernel recv maximum buffer size for the rtp socket.\n *\tA value of zero defaults to the operating system default.\n **/\nvoid rtp_session_set_rtp_socket_recv_buffer_size(RtpSession *session, unsigned int size) {\n\tsession->rtp.rcv_socket_size = size;\n\t_rtp_session_apply_socket_sizes(session);\n}\n\n/**\n *\tThis function provides the way for an application to be informed of various events that\n *\tmay occur during a rtp session. \\a signal_name is a string identifying the event, and \\a cb is\n *\ta user supplied function in charge of processing it. The application can register\n *\tseveral callbacks for the same signal, in the limit of \\a RTP_CALLBACK_TABLE_MAX_ENTRIES.\n *\tHere are name and meaning of supported signals types:\n *\n *\t\"ssrc_changed\": the SSRC of the incoming stream has changed.\n *\n *\t\"payload_type_changed\": the payload type of the incoming stream has changed.\n *\n *\t\"telephone-event_packet\": a telephone-event rtp packet (RFC2833) is received.\n *\n *\t\"telephone-event\": a telephone event has occurred. This is a high-level shortcut for \"telephone-event_packet\".\n *\n *\t\"network_error\": a network error happened on a socket. Arguments of the callback functions are\n *\t\t\t\t\t\ta const char * explaining the error, an int errno error code and the user_data as usual.\n *\n *\t\"timestamp_jump\": we have received a packet with timestamp in far future compared to last timestamp received.\n *\t\t\t\t\t\tThe farness of far future is set by rtp_sesssion_set_time_jump_limit()\n *  \"rtcp_bye\": we have received a RTCP bye packet. Arguments of the callback\n *              functions are a const char * containing the leaving reason and\n *              the user_data.\n *\t\"congestion_state_changed\": congestion detector object changed its internal state. Arguments of\n *\t\t\t\t\t\t\t\tthe callback function are previous and new states.\n *\t\"new_incoming_ssrc_found_in_bundle\": a new SSRC is detected in the bundle dispatch and no sessions are free to\n *attach it. Arguments are:\n *\t\t\t\t\t- a mblk_t pointer to the incoming packet\n *\t\t\t\t\t- a pointer to an RtpSession pointer so the callback can create a new RtpSession and pass it back.\n *\t\"new_outgoing_ssrc_found_in_bundle\": a new SSRC is detected in the bundle while sending a packet\n *\t\t\t\tArguments are:\n *\t\t\t\t\t- a mblk_t pointer to the outgoing packet\n *\t\t\t\t\t- a pointer to an RtpSession pointer so the callback can create a new RtpSession and pass it back.\n *\n * @param session \ta rtp session\n * @param signal_name\tthe name of a signal\n * @param cb\t\ta RtpCallback\n * @param user_data\ta pointer to any data to be passed when invoking the callback.\n * @return 0 on success, -EOPNOTSUPP if the signal does not exists, -1 if no more callbacks can be assigned to the\n *signal type.\n *\n **/\nint rtp_session_signal_connect(RtpSession *session, const char *signal_name, RtpCallback cb, void *user_data) {\n\treturn rtp_session_signal_connect_from_source_session(session, signal_name, cb, user_data, NULL);\n}\n\n/**\n *\tConnect the callback \\a cb to the signal \\a signal_name as \\a rtp_session_signal_connect.\n *\tThis is used to provide the source session in case the callback to add is coming from an other one.\n *\n * @param session \ta rtp session\n * @param signal_name\tthe name of a signal\n * @param cb\t\ta RtpCallback\n * @param user_data\ta pointer to any data to be passed when invoking the callback.\n * @param source the source session of the callback, NULL if the current session is the source\n * @return 0 on success, -EOPNOTSUPP if the signal does not exists, -1 if no more callbacks can be assigned to the\n *signal type.\n **/\nint rtp_session_signal_connect_from_source_session(\n    RtpSession *session, const char *signal_name, RtpCallback cb, void *user_data, const RtpSession *source) {\n\tbctbx_list_t *elem;\n\tfor (elem = session->signal_tables; elem != NULL; elem = o_list_next(elem)) {\n\t\tRtpSignalTable *s = (RtpSignalTable *)elem->data;\n\t\tif (strcmp(signal_name, s->signal_name) == 0) {\n\t\t\treturn rtp_signal_table_add_from_source_session(s, cb, user_data, source);\n\t\t}\n\t}\n\tortp_warning(\"rtp_session_signal_connect: inexistent signal %s\", signal_name);\n\treturn -1;\n}\n\n/**\n *\tRemoves callback function \\a cb to the list of callbacks for signal \\a signal.\n *\n * @param session a rtp session\n * @param signal_name\ta signal name\n * @param cb\ta callback function.\n * @return: 0 on success, a negative value if the callback was not found.\n **/\nint rtp_session_signal_disconnect_by_callback(RtpSession *session, const char *signal_name, RtpCallback cb) {\n\tOList *elem;\n\tfor (elem = session->signal_tables; elem != NULL; elem = o_list_next(elem)) {\n\t\tRtpSignalTable *s = (RtpSignalTable *)elem->data;\n\t\tif (strcmp(signal_name, s->signal_name) == 0) {\n\t\t\treturn rtp_signal_table_remove_by_callback(s, cb);\n\t\t}\n\t}\n\tortp_warning(\"rtp_session_signal_connect: inexistant signal %s\", signal_name);\n\treturn -1;\n}\n\n/**\n *\tRemoves callback function \\a cb to the list of callbacks for signal \\a signal with user data \\a user_data.\n *\n * @param session a rtp session\n * @param signal_name\ta signal name\n * @param cb\ta callback function.\n * @param user_data the user data.\n * @return: 0 on success, a negative value if the callback was not found.\n **/\nint rtp_session_signal_disconnect_by_callback_and_user_data(RtpSession *session,\n                                                            const char *signal_name,\n                                                            RtpCallback cb,\n                                                            void *user_data) {\n\tOList *elem;\n\tfor (elem = session->signal_tables; elem != NULL; elem = o_list_next(elem)) {\n\t\tRtpSignalTable *s = (RtpSignalTable *)elem->data;\n\t\tif (strcmp(signal_name, s->signal_name) == 0) {\n\t\t\treturn rtp_signal_table_remove_by_callback_and_user_data(s, cb, user_data);\n\t\t}\n\t}\n\tortp_warning(\"rtp_session_signal_connect: inexistant signal %s\", signal_name);\n\treturn -1;\n}\n\n/**\n *\tRemoves callbacks functions to the list of callbacks for signal \\a signal from the source session \\a source.\n *\n * @param session a rtp session\n * @param signal_name\ta signal name\n * @param source\tthe source session.\n * @return: 0 on success, a negative value if the callback was not found.\n **/\nint rtp_session_signal_disconnect_by_source_session(RtpSession *session,\n                                                    const char *signal_name,\n                                                    const RtpSession *source) {\n\tOList *elem;\n\tfor (elem = session->signal_tables; elem != NULL; elem = o_list_next(elem)) {\n\t\tRtpSignalTable *s = (RtpSignalTable *)elem->data;\n\t\tif (strcmp(signal_name, s->signal_name) == 0) {\n\t\t\treturn rtp_signal_table_remove_by_source_session(s, source);\n\t\t}\n\t}\n\tortp_warning(\"rtp_session_signal_connect: inexistant signal %s\", signal_name);\n\treturn -1;\n}\n\n/**\n * Set the initial sequence number for outgoing stream..\n * @param session\t\ta rtp session freshly created.\n * @param seq\t\t\ta 16 bit unsigned number.\n *\n **/\nvoid rtp_session_set_seq_number(RtpSession *session, uint16_t seq) {\n\tsession->rtp.snd_seq = seq;\n}\n\nvoid rtp_session_set_duplication_ratio(RtpSession *session, float ratio) {\n\tsession->duplication_ratio = ratio;\n}\n\n/**\n * Get the current sequence number for outgoing stream.\n **/\nuint16_t rtp_session_get_seq_number(RtpSession *session) {\n\treturn session->rtp.snd_seq;\n}\n\n/**\n * Returns the highest extended sequence number received.\n **/\nuint32_t rtp_session_get_rcv_ext_seq_number(RtpSession *session) {\n\treturn session->rtp.hwrcv_extseq;\n}\n\n/**\n * Returns the latest cumulative loss value computed\n **/\nint rtp_session_get_cum_loss(RtpSession *session) {\n\treturn session->cum_loss;\n}\n\n/**\n *\tSets the SSRC for the outgoing stream.\n *  If not done, a random ssrc is used.\n *\n * @param session a rtp session.\n * @param ssrc an unsigned 32bit integer representing the synchronisation source identifier (SSRC).\n **/\nvoid rtp_session_set_ssrc(RtpSession *session, uint32_t ssrc) {\n\tsession->snd.ssrc = ssrc;\n}\n\n/**\n *\tGet the SSRC for the outgoing stream.\n *\n * @param session a rtp session.\n **/\nuint32_t rtp_session_get_send_ssrc(const RtpSession *session) {\n\treturn session->snd.ssrc;\n}\n\n/**\n * Get the SSRC for the incoming stream.\n *\n * If no packets have been received yet, 0 is returned.\n **/\nuint32_t rtp_session_get_recv_ssrc(RtpSession *session) {\n\treturn session->rcv.ssrc;\n}\n\nvoid rtp_session_update_payload_type(RtpSession *session, int paytype) {\n\t/* check if we support this payload type */\n\tPayloadType *pt = rtp_profile_get_payload(session->rcv.profile, paytype);\n\tif (pt != 0) {\n\t\tsession->hw_recv_pt = paytype;\n\t\tortp_message(\"payload type changed to %i(%s) !\", paytype, pt->mime_type);\n\t\tpayload_type_changed(session, pt);\n\t} else {\n\t\tortp_warning(\"Receiving packet with unknown payload type %i.\", paytype);\n\t}\n}\n/**\n *\tSets the payload type of the rtp session. It decides of the payload types written in the\n *\tof the rtp header for the outgoing stream, if the session is SENDRECV or SENDONLY.\n *\tFor payload type in incoming packets, the application can be informed by registering\n *\tfor the \"payload_type_changed\" signal, so that it can make the necessary changes\n *\ton the downstream decoder that deals with the payload of the packets.\n *\n * @param session a rtp session\n * @param paytype the payload type number\n * @return 0 on success, -1 if the payload is not defined.\n **/\n\nint rtp_session_set_send_payload_type(RtpSession *session, int paytype) {\n\tsession->snd.pt = paytype;\n\treturn 0;\n}\n\n/**\n *@param session a rtp session\n *\n *@return the payload type currently used in outgoing rtp packets\n **/\nint rtp_session_get_send_payload_type(const RtpSession *session) {\n\treturn session->snd.pt;\n}\n\n/**\n * Assign the payload type number for sending telephone-event.\n * It is required that a \"telephone-event\" PayloadType is assigned in the RtpProfile set for the RtpSession.\n * This function is in most of cases useless, unless there is an ambiguity where several PayloadType for\n *\"telephone-event\" are present in the RtpProfile. This might happen during SIP offeranswer scenarios. This function\n *allows to remove any ambiguity by letting the application choose the one to be used.\n * @param session the RtpSession\n * @param paytype the payload type number\n * @returns 0, -1 on error.\n **/\nint rtp_session_set_send_telephone_event_payload_type(RtpSession *session, int paytype) {\n\tsession->tev_send_pt = paytype;\n\treturn 0;\n}\n\n/**\n *\n *\tSets the expected payload type for incoming packets.\n *\tIf the actual payload type in incoming packets is different that this expected payload type, thus\n *\tthe \"payload_type_changed\" signal is emitted.\n *\n *@param session a rtp session\n *@param paytype the payload type number\n *@return 0 on success, -1 if the payload is not defined.\n **/\n\nint rtp_session_set_recv_payload_type(RtpSession *session, int paytype) {\n\tPayloadType *pt;\n\tsession->rcv.pt = paytype;\n\tsession->hw_recv_pt = paytype;\n\tpt = rtp_profile_get_payload(session->rcv.profile, paytype);\n\tif (pt != NULL) {\n\t\tpayload_type_changed(session, pt);\n\t}\n\treturn 0;\n}\n\n/**\n *@param session a rtp session\n *\n * @return the payload type currently used in incoming rtp packets\n **/\nint rtp_session_get_recv_payload_type(const RtpSession *session) {\n\treturn session->rcv.pt;\n}\n\n/**\n *\tSets the expected payload type for incoming packets and payload type to be used for outgoing packets.\n *\tIf the actual payload type in incoming packets is different that this expected payload type, thus\n *\tthe \"payload_type_changed\" signal is emitted.\n *\n * @param session a rtp session\n * @param pt the payload type number\n * @return 0 on success, -1 if the payload is not defined.\n **/\nint rtp_session_set_payload_type(RtpSession *session, int pt) {\n\tif (rtp_session_set_send_payload_type(session, pt) < 0) return -1;\n\tif (rtp_session_set_recv_payload_type(session, pt) < 0) return -1;\n\treturn 0;\n}\n\nstatic void rtp_header_init_from_session(rtp_header_t *rtp, RtpSession *session) {\n\trtp->version = 2;\n\trtp->padbit = 0;\n\trtp->extbit = 0;\n\trtp->markbit = 0;\n\trtp->cc = 0;\n\trtp->paytype = session->snd.pt;\n\trtp_header_set_ssrc(rtp, session->snd.ssrc);\n\trtp->timestamp = 0; /* set later, when packet is sended */\n\t/* set a seq number */\n\trtp_header_set_seqnumber(rtp, session->rtp.snd_seq);\n}\n\n/**\n *\tReturns the size of the header a new rtp packet should have using the provided session.\n *\n *@param cc the number of contributing sources.\n *@param mid the mid of this session if the bundle mode is enabled.\n *@return the header size.\n **/\nsize_t rtp_session_calculate_packet_header_size(int cc, const char *mid) {\n\tsize_t header_size = RTP_FIXED_HEADER_SIZE;\n\n\theader_size += cc * sizeof(uint32_t);\n\n\t// Calculate size for mid\n\tif (mid != NULL) {\n\t\tsize_t mid_size = strlen(mid);\n\t\tsize_t padding = (mid_size + 1) % 4 != 0 ? (4 - (mid_size + 1) % 4) : 0;\n\t\theader_size += strlen(mid) + 5 + padding;\n\t}\n\n\treturn header_size;\n}\n\nstatic void rtp_header_add_csrcs_from_session(rtp_header_t *header, RtpSession *session) {\n\tmblk_t *sdes;\n\tfor (sdes = qbegin(&session->contributing_sources); !qend(&session->contributing_sources, sdes);\n\t     sdes = qnext(&session->contributing_sources, sdes)) {\n\t\trtp_header_add_csrc(header, sdes_chunk_get_ssrc(sdes));\n\t}\n}\n\nstatic bool_t rtp_session_should_send_mid(RtpSession *session) {\n\tif (session->mid_sent < RTP_BUNDLE_MAX_SENT_MID_START) {\n\t\tsession->mid_sent++;\n\t\treturn TRUE;\n\t}\n\n\tconst uint64_t now = bctbx_get_cur_time_ms();\n\tif (now - session->last_mid_sent_time > RTP_BUNDLE_MID_SENDING_INTERVAL) {\n\t\tsession->last_mid_sent_time = now;\n\t\treturn TRUE;\n\t}\n\n\treturn FALSE;\n}\n\nmblk_t *rtp_session_create_packet_header(RtpSession *session, size_t extra_header_size) {\n\tmblk_t *mp;\n\trtp_header_t *rtp;\n\tsize_t header_size;\n\tchar *mid = NULL;\n\n\tortp_mutex_lock(&session->main_mutex);\n\tif (session->bundle && rtp_session_should_send_mid(session)) {\n\t\tmid = rtp_bundle_get_session_mid(session->bundle, session);\n\t}\n\n\theader_size = rtp_session_calculate_packet_header_size(session->contributing_sources.q_mcount, mid);\n\n\tmp = allocb(header_size + extra_header_size, BPRI_MED);\n\trtp = (rtp_header_t *)mp->b_rptr;\n\trtp_header_init_from_session(rtp, session);\n\n\trtp_header_add_csrcs_from_session(rtp, session);\n\n\t/*add the mid from the bundle if any*/\n\n\tif (mid != NULL) {\n\t\tint midId = rtp_bundle_get_mid_extension_id(session->bundle);\n\t\t// storage is already allocated so use rtp_insert instead of rtp_add\n\t\trtp_write_extension_header(mp, midId != -1 ? midId : RTP_EXTENSION_MID, strlen(mid), (uint8_t *)mid);\n\t\tbctbx_free(mid);\n\t}\n\n\tortp_mutex_unlock(&session->main_mutex);\n\tmp->b_wptr += header_size;\n\n\treturn mp;\n}\n\n/** This one is nearly identical to the simple one, just add a CSRC fetched from sourceSession */\nmblk_t *\nrtp_session_create_repair_packet_header(RtpSession *fecSession, RtpSession *sourceSession, size_t extra_header_size) {\n\tmblk_t *mp;\n\trtp_header_t *rtp;\n\tchar *mid = NULL;\n\tsize_t header_size;\n\n\tortp_mutex_lock(&fecSession->main_mutex);\n\tif (fecSession->bundle && rtp_session_should_send_mid(fecSession)) {\n\t\tmid = rtp_bundle_get_session_mid(fecSession->bundle, fecSession);\n\t}\n\n\theader_size = rtp_session_calculate_packet_header_size(fecSession->contributing_sources.q_mcount, mid) +\n\t              4; /* +4 for the extra CSRC*/\n\n\tmp = allocb(header_size + extra_header_size, BPRI_MED);\n\trtp = (rtp_header_t *)mp->b_rptr;\n\trtp_header_init_from_session(rtp, fecSession);\n\n\t/* add the sourceSession SSRC as CSRC */\n\trtp_header_add_csrc(rtp, rtp_session_get_send_ssrc(sourceSession));\n\n\t/*add the mid from the bundle if any*/\n\tif (mid != NULL) {\n\t\tint midId = rtp_bundle_get_mid_extension_id(fecSession->bundle);\n\t\t// storage is already allocated so use rtp_insert instead of rtp_add\n\t\trtp_write_extension_header(mp, midId != -1 ? midId : RTP_EXTENSION_MID, strlen(mid), (uint8_t *)mid);\n\t\tbctbx_free(mid);\n\t}\n\n\tortp_mutex_unlock(&fecSession->main_mutex);\n\tmp->b_wptr += header_size;\n\n\treturn mp;\n}\n\nmblk_t *rtp_session_create_packet_header_with_mixer_to_client_audio_level(RtpSession *session,\n                                                                          size_t extra_header_size,\n                                                                          int mtc_extension_id,\n                                                                          size_t audio_levels_size,\n                                                                          rtp_audio_level_t *audio_levels) {\n\tmblk_t *mp;\n\trtp_header_t *rtp;\n\tchar *mid = NULL;\n\n\t/** Compute the header size **/\n\tsize_t header_size = RTP_FIXED_HEADER_SIZE;\n\tsize_t ext_header_size = 0;\n\n\t// Calculate size for mixer to client volume (csrc + extension header)\n\tif (audio_levels_size > 0) {\n\t\text_header_size += audio_levels_size + 1;            // Extension: compute the total extension header size\n\t\theader_size += audio_levels_size * sizeof(uint32_t); // CSRCs, add it directly to the header size\n\t}\n\n\tortp_mutex_lock(&session->main_mutex);\n\t// Calculate ext header size for mid\n\tif (session->bundle && rtp_session_should_send_mid(session)) {\n\t\tmid = rtp_bundle_get_session_mid(session->bundle, session);\n\n\t\tif (mid != NULL) {\n\t\t\text_header_size += strlen(mid) + 1;\n\t\t}\n\t}\n\n\tif (ext_header_size > 0) {\n\t\tsize_t padding =\n\t\t    ext_header_size % 4 == 0 ? 0 : (4 - (ext_header_size % 4)); // is padding needed on the extension header?\n\t\theader_size += 4 + ext_header_size + padding;                   // +4 for the extension header\n\t}\n\n\tmp = allocb(header_size + extra_header_size, BPRI_MED);\n\trtp = (rtp_header_t *)mp->b_rptr;\n\trtp_header_init_from_session(rtp, session);\n\n\t/* wptr is already set at the end of the header we're about to write\n\t * so when writing the bundle ext header it will find correctly the already present mixer to client ext header */\n\tmp->b_wptr += header_size;\n\n\t/* write the mixer to client audio level first so it takes care of CSRC before writing more ext header\n\t * memory is already allocated so use the write function not add */\n\trtp_write_mixer_to_client_audio_level(mp, mtc_extension_id, audio_levels_size, audio_levels);\n\n\t/*add the mid from the bundle if any*/\n\tif (session->bundle && mid != NULL) {\n\t\tint midId = rtp_bundle_get_mid_extension_id(session->bundle);\n\t\trtp_write_extension_header(mp, midId != -1 ? midId : RTP_EXTENSION_MID, strlen(mid), (uint8_t *)mid);\n\t\tbctbx_free(mid);\n\t}\n\tortp_mutex_unlock(&session->main_mutex);\n\n\treturn mp;\n}\n/** create a packet from the given buffer. No header is added, the buffer is copied in a mblk_t allocated for this\n * purpose use to create non RTP packets (ZRTP, DTLS, STUN) or set a payload in a message (for CNG for example)\n * @param[in] packet\t\tpointer to the data to be copied in the created packet\n * @param[in] packet_size\tsize of data buffer\n *\n * @return a packet in a message block structure holding the given buffer\n */\nmblk_t *rtp_create_packet(const uint8_t *packet, size_t packet_size) {\n\tmblk_t *mp;\n\n\tmp = allocb(packet_size, BPRI_MED);\n\tif (packet_size) {\n\t\tmemcpy(mp->b_wptr, packet, packet_size);\n\t\tmp->b_wptr += packet_size;\n\t}\n\treturn mp;\n}\n\n/** create a packet from the given buffer. No header is added, the buffer is not copied but integrated to the packet\n * @param[in] packet\t\tpointer to the data to be copied in the created packet\n * @param[in] packet_size\tsize of data buffer\n * @param[in] freefn\t\ta function that will be called when the payload buffer is no more needed.\n *\n * @return a packet in a message block structure holding the given buffer\n */\nmblk_t *rtp_package_packet(uint8_t *packet, size_t packet_size, void (*freefn)(void *)) {\n\t/* create a mblk_t around the user supplied payload buffer */\n\tmblk_t *mp = esballoc(packet, packet_size, BPRI_MED, freefn);\n\tmp->b_wptr += packet_size;\n\treturn mp;\n}\n\n/******************* DEPRECATED packet creations functions ************************************\n * Do not create any more the whole packet including payload. The correct way to do that is:\n *    - create the packet header with rtp_session_create_packet_header<_XXX> function\n *    - optionnally add further extension headers\n *    - if raw payload is not available in a mblk_t but in a buffer, use rtp_create_packet_copy\n *      or rtp_create_packet_package\n *    - concatenate the payload to the packet header created using b_cont\n *\n **********************************************************************************************/\n\n/**\n *\tAllocates a new rtp packet. In the header, ssrc and payload_type according to the session's\n *\tcontext. Timestamp is not set, it will be set when the packet is going to be\n *\tsent with rtp_session_sendm_with_ts(). Sequence number is initalized to previous sequence number sent + 1\n *\tIf payload_size is zero, thus an empty packet (just a RTP header) is returned.\n *\n *@param session a rtp session.\n *@param header_size the rtp header size. For standart size (without extensions), it is RTP_FIXED_HEADER_SIZE. If set to\n *0, it will be calculated automatically.\n *@param payload data to be copied into the rtp packet.\n *@param payload_size size of data carried by the rtp packet.\n *@return a rtp packet in a mblk_t (message block) structure.\n **/\nmblk_t *\nrtp_session_create_packet(RtpSession *session, size_t header_size, const uint8_t *payload, size_t payload_size) {\n\tmblk_t *mp;\n\tsize_t msglen = payload_size;\n\trtp_header_t *rtp;\n\tsize_t computed_header_size;\n\tchar *mid = NULL;\n\n\tortp_mutex_lock(&session->main_mutex);\n\n\tif (session->bundle && rtp_session_should_send_mid(session)) {\n\t\tmid = rtp_bundle_get_session_mid(session->bundle, session);\n\t}\n\n\tcomputed_header_size = rtp_session_calculate_packet_header_size(session->contributing_sources.q_mcount, mid);\n\n\tif (computed_header_size > header_size) {\n\t\theader_size = computed_header_size;\n\t}\n\n\tmsglen += header_size;\n\n\tmp = allocb(msglen, BPRI_MED);\n\trtp = (rtp_header_t *)mp->b_rptr;\n\trtp_header_init_from_session(rtp, session);\n\n\trtp_header_add_csrcs_from_session(rtp, session);\n\n\t/*add the mid from the bundle if any*/\n\tif (mid != NULL) {\n\t\tint midId = rtp_bundle_get_mid_extension_id(session->bundle);\n\t\t// storage is already allocated so use rtp_insert instead of rtp_add\n\t\trtp_write_extension_header(mp, midId != -1 ? midId : RTP_EXTENSION_MID, strlen(mid), (uint8_t *)mid);\n\t\tbctbx_free(mid);\n\t}\n\n\tortp_mutex_unlock(&session->main_mutex);\n\tmp->b_wptr += header_size;\n\n\t/*copy the payload, if any */\n\tif (payload_size) {\n\t\tmemcpy(mp->b_wptr, payload, payload_size);\n\t\tmp->b_wptr += payload_size;\n\t}\n\treturn mp;\n}\n\n/**\n *\tThis will do the same as rtp_session_create_packet() without payload data but it will also add\n *\tmixer to client audio level indication through header extensions.\n *\n *@param session a rtp session.\n *@param header_size the rtp header size. For standart size (without extensions), it is RTP_FIXED_HEADER_SIZE\n *@param mtc_extension_id id of the mixer to client extension id.\n *@param audio_levels_size size of audio levels contained in audio_levels parameter.\n *@param audio_levels list of rtp_audio_level_t to add in this packet.\n *@param payload data to be copied into the rtp packet.\n *@param payload_size size of data carried by the rtp packet.\n *@return a rtp packet in a mblk_t (message block) structure.\n **/\nmblk_t *rtp_session_create_packet_with_mixer_to_client_audio_level(RtpSession *session,\n                                                                   size_t header_size,\n                                                                   int mtc_extension_id,\n                                                                   size_t audio_levels_size,\n                                                                   rtp_audio_level_t *audio_levels,\n                                                                   const uint8_t *payload,\n                                                                   size_t payload_size) {\n\tmblk_t *mp;\n\tsize_t msglen = payload_size;\n\trtp_header_t *rtp;\n\tchar *mid = NULL;\n\n\t/** Compute the header size **/\n\tsize_t computed_header_size = RTP_FIXED_HEADER_SIZE;\n\tsize_t ext_header_size = 0;\n\n\t// Calculate size for mixer to client volume (csrc + extension header)\n\tif (audio_levels_size > 0) {\n\t\text_header_size += audio_levels_size + 1; // Extension: compute the total extension header size\n\t\tcomputed_header_size += audio_levels_size * sizeof(uint32_t); // CSRCs, add it directly to the header size\n\t}\n\n\t// Calculate ext header size for mid\n\tortp_mutex_lock(&session->main_mutex);\n\tif (session->bundle && rtp_session_should_send_mid(session)) {\n\t\tmid = rtp_bundle_get_session_mid(session->bundle, session);\n\n\t\tif (mid != NULL) {\n\t\t\text_header_size += strlen(mid) + 1;\n\t\t}\n\t}\n\n\tif (ext_header_size > 0) {\n\t\tsize_t padding =\n\t\t    ext_header_size % 4 == 0 ? 0 : (4 - (ext_header_size % 4)); // is padding needed on the extension header?\n\t\tcomputed_header_size += 4 + ext_header_size + padding;\n\t}\n\n\t/** use the biggest of computed and given header size **/\n\tif (computed_header_size > header_size) {\n\t\theader_size = computed_header_size;\n\t}\n\n\tmsglen += header_size;\n\n\tmp = allocb(msglen, BPRI_MED);\n\trtp = (rtp_header_t *)mp->b_rptr;\n\trtp_header_init_from_session(rtp, session);\n\n\t/* wptr is already set at the end of the header we're about to write\n\t * so when writing the bundle ext header it will find correctly the already present mtc ext header */\n\tmp->b_wptr += header_size;\n\n\t/* write the mixer to client audio level first so it takes care of CSRC before writing more ext header\n\t * memory is already allocated so use the write function not add */\n\trtp_write_mixer_to_client_audio_level(mp, mtc_extension_id, audio_levels_size, audio_levels);\n\n\t/*add the mid from the bundle if any*/\n\tif (session->bundle && mid != NULL) {\n\t\tint midId = rtp_bundle_get_mid_extension_id(session->bundle);\n\t\trtp_write_extension_header(mp, midId != -1 ? midId : RTP_EXTENSION_MID, strlen(mid), (uint8_t *)mid);\n\t\tbctbx_free(mid);\n\t}\n\tortp_mutex_unlock(&session->main_mutex);\n\n\t/*copy the payload, if any */\n\tif (payload_size) {\n\t\tmemcpy(mp->b_wptr, payload, payload_size);\n\t\tmp->b_wptr += payload_size;\n\t}\n\n\treturn mp;\n}\n\n/**\n * Create a packet already including headers\n */\nmblk_t *rtp_session_create_packet_raw(const uint8_t *packet, size_t packet_size) {\n\tmblk_t *mp;\n\n\tmp = allocb(packet_size, BPRI_MED);\n\tif (packet_size) {\n\t\tmemcpy(mp->b_wptr, packet, packet_size);\n\t\tmp->b_wptr += packet_size;\n\t}\n\treturn mp;\n}\n\n/**\n *\tCreates a new rtp packet using the given payload buffer (no copy). The header will be allocated separetely.\n *  In the header, ssrc and payload_type according to the session's\n *\tcontext. Timestamp and seq number are not set, there will be set when the packet is going to be\n *\tsent with rtp_session_sendm_with_ts().\n *\toRTP will send this packet using libc's sendmsg() (if this function is availlable!) so that there will be no\n *\tpacket concatenation involving copies to be done in user-space.\n *  \\a freefn can be NULL, in that case payload will be kept untouched.\n *\n * @param session a rtp session.\n * @param payload the data to be sent with this packet\n * @param payload_size size of data\n * @param freefn a function that will be called when the payload buffer is no more needed.\n * @return: a rtp packet in a mblk_t (message block) structure.\n **/\n\nmblk_t *rtp_session_create_packet_with_data(RtpSession *session,\n                                            uint8_t *payload,\n                                            size_t payload_size,\n                                            void (*freefn)(void *)) {\n\tmblk_t *mp, *mpayload;\n\tsize_t header_size;\n\trtp_header_t *rtp;\n\tchar *mid = NULL;\n\n\tortp_mutex_lock(&session->main_mutex);\n\tif (session->bundle && rtp_session_should_send_mid(session)) {\n\t\tmid = rtp_bundle_get_session_mid(session->bundle, session);\n\t}\n\n\theader_size = rtp_session_calculate_packet_header_size(session->contributing_sources.q_mcount, mid);\n\n\tmp = allocb(header_size, BPRI_MED);\n\trtp = (rtp_header_t *)mp->b_rptr;\n\trtp_header_init_from_session(rtp, session);\n\n\trtp_header_add_csrcs_from_session(rtp, session);\n\n\t/*add the mid from the bundle if any*/\n\tif (mid != NULL) {\n\t\tint midId = rtp_bundle_get_mid_extension_id(session->bundle);\n\t\t// storage is already allocated so use rtp_insert instead of rtp_add\n\t\trtp_write_extension_header(mp, midId != -1 ? midId : RTP_EXTENSION_MID, strlen(mid), (uint8_t *)mid);\n\t\tbctbx_free(mid);\n\t}\n\tortp_mutex_unlock(&session->main_mutex);\n\n\tmp->b_wptr += header_size;\n\n\t/* create a mblk_t around the user supplied payload buffer */\n\tmpayload = esballoc(payload, payload_size, BPRI_MED, freefn);\n\tmpayload->b_wptr += payload_size;\n\t/* link it with the header */\n\tmp->b_cont = mpayload;\n\treturn mp;\n}\n/******************* end of DEPRECATED packet creations functions *****************************/\n\nstatic int __rtp_session_sendm_with_ts_2(\n    RtpSession *session, mblk_t *mp, uint32_t packet_ts, uint32_t send_ts, bool_t transfer_set_mid) {\n\trtp_header_t *rtp;\n\tuint32_t packet_time;\n\tint error = 0;\n\tsize_t packsize;\n\tRtpScheduler *sched = session->sched;\n\tRtpStream *stream = &session->rtp;\n\n\tif (session->flags & RTP_SESSION_SEND_NOT_STARTED) {\n\t\tsession->rtp.snd_ts_offset = send_ts;\n\t\t/* Set initial last_rcv_time to first send time. */\n\t\tif ((session->flags & RTP_SESSION_RECV_NOT_STARTED) || session->mode == RTP_SESSION_SENDONLY) {\n\t\t\tbctbx_gettimeofday(&session->last_recv_time, NULL);\n\t\t}\n\t\tif (session->flags & RTP_SESSION_SCHEDULED) {\n\t\t\tsession->rtp.snd_time_offset = sched->time_;\n\t\t}\n\t\trtp_session_unset_flag(session, RTP_SESSION_SEND_NOT_STARTED);\n\t}\n\t/* if we are in blocking mode, then suspend the process until the scheduler it's time to send  the\n\t * next packet */\n\t/* if the timestamp of the packet queued is older than current time, then you we must\n\t * not block */\n\tif (session->flags & RTP_SESSION_SCHEDULED) {\n\t\twait_point_lock(&session->snd.wp);\n\t\tpacket_time =\n\t\t    rtp_session_ts_to_time(session, send_ts - session->rtp.snd_ts_offset) + session->rtp.snd_time_offset;\n\t\t/*ortp_message(\"rtp_session_send_with_ts: packet_time=%i time=%i\",packet_time,sched->time_);*/\n\t\tif (TIME_IS_STRICTLY_NEWER_THAN(packet_time, sched->time_)) {\n\t\t\twait_point_wakeup_at(&session->snd.wp, packet_time, (session->flags & RTP_SESSION_BLOCKING_MODE) != 0);\n\t\t\tsession_set_clr(&sched->w_sessions, session);    /* the session has written */\n\t\t} else session_set_set(&sched->w_sessions, session); /*to indicate select to return immediately */\n\t\twait_point_unlock(&session->snd.wp);\n\t}\n\n\tif (mp == NULL) { /*for people who just want to be blocked but\n\t\t     do not want to send anything.*/\n\t\tsession->rtp.snd_last_ts = packet_ts;\n\t\treturn 0;\n\t}\n\n\trtp = (rtp_header_t *)mp->b_rptr;\n\n\tpacksize = msgdsize(mp);\n\tsession->duplication_left += session->duplication_ratio;\n\tif (rtp->version == 0) {\n\t\t/* We are probably trying to send a STUN packet so don't change its content. */\n\t} else {\n\t\tif (!session->transfer_mode) {\n\t\t\trtp_header_set_timestamp(rtp, packet_ts);\n\t\t} else {\n\t\t\t/* when in transfer mode, packet was created not for this session so we may have to adjust the bundle\n\t\t\t        mode mid extension */\n\t\t\t/*add the mid from the bundle if any*/\n\t\t\tortp_mutex_lock(&session->main_mutex);\n\t\t\tif (session->bundle) {\n\t\t\t\tRtpSession *bundle_session = NULL;\n\t\t\t\tRtpBundle *bundle = session->bundle;\n\t\t\t\tif (transfer_set_mid == TRUE) {\n\t\t\t\t\tchar *mid = rtp_bundle_get_session_mid(bundle, session);\n\n\t\t\t\t\tif (mid != NULL) {\n\t\t\t\t\t\t/* ensure the paquet MID is matching the bundle one */\n\t\t\t\t\t\tint midId = rtp_bundle_get_mid_extension_id(bundle);\n\t\t\t\t\t\trtp_add_extension_header(mp, midId != -1 ? midId : RTP_EXTENSION_MID, strlen(mid),\n\t\t\t\t\t\t                         (uint8_t *)mid);\n\t\t\t\t\t\trtp =\n\t\t\t\t\t\t    (rtp_header_t *)\n\t\t\t\t\t\t        mp->b_rptr; // rtp_add_extension might pullup the message so reset the pointer to header\n\n\t\t\t\t\t\tbctbx_free(mid);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tortp_mutex_unlock(&session->main_mutex);\n\t\t\t\t/* in transfer mode, if we are part of a bundle, check this is the correct session to send this packet\n\t\t\t\t */\n\t\t\t\tbundle_session = rtp_bundle_lookup_session_for_outgoing_packet(bundle, mp);\n\t\t\t\tif (bundle_session != NULL && bundle_session != session) {\n\t\t\t\t\t/* recursive call but skip the part where we set the mid extension in the packet */\n\t\t\t\t\treturn __rtp_session_sendm_with_ts_2(bundle_session, mp, packet_ts, send_ts, FALSE);\n\t\t\t\t}\n\t\t\t} else ortp_mutex_unlock(&session->main_mutex);\n\t\t}\n\n\t\t/* When in transfer mode, force the actual seq number to the session one, as we must ensure sequence number\n\t\t * continuity But the original one may be needed by SRTP double encryption mode, so save it in the packet\n\t\t * Same thing with the payload type: we must use the current session one but save the original one */\n\t\tif (session->transfer_mode) {\n\t\t\tortp_mblk_set_original_seqnum(mp, rtp_header_get_seqnumber(rtp));\n\t\t\tortp_mblk_set_original_pt(mp, rtp->paytype);\n\t\t\trtp_header_set_seqnumber(rtp, session->rtp.snd_seq);\n\t\t\trtp_set_payload_type(mp, session->snd.pt);\n\n\t\t\tsession->rtp.snd_seq++;\n\t\t} else {\n\t\t\tif (rtp_profile_is_telephone_event(session->snd.profile, rtp->paytype)) {\n\t\t\t\trtp_header_set_seqnumber(rtp, session->rtp.snd_seq);\n\t\t\t\tsession->rtp.snd_seq++;\n\t\t\t} else {\n\t\t\t\tsession->rtp.snd_seq = rtp_header_get_seqnumber(rtp) + 1;\n\t\t\t}\n\t\t}\n\t\tsession->rtp.snd_last_ts = packet_ts;\n\n\t\tstream->sent_payload_bytes += (uint32_t)(packsize - RTP_FIXED_HEADER_SIZE);\n\n\t\tortp_global_stats.sent += (1 + (int)session->duplication_left) * packsize;\n\t\tsession->stats.sent += (1 + (int)session->duplication_left) * packsize;\n\n\t\tortp_global_stats.packet_sent++;\n\t\tsession->stats.packet_sent++;\n\n\t\tsession->stats.packet_dup_sent += (int)session->duplication_left;\n\t\tortp_global_stats.packet_sent += (int)session->duplication_left;\n\t}\n\n\twhile (session->duplication_left >= 1.f) {\n\t\terror = rtp_session_rtp_send(session, copymsg(mp));\n\t\tsession->duplication_left -= 1.f;\n\t}\n\n\terror = rtp_session_rtp_send(session, mp);\n\n\t/*send RTCP packet if needed */\n\trtp_session_run_rtcp_send_scheduler(session);\n\t/* receives rtcp packet if session is send-only*/\n\t/*otherwise it is done in rtp_session_recvm_with_ts */\n\tif (session->mode == RTP_SESSION_SENDONLY) rtp_session_rtcp_recv(session);\n\n\treturn error;\n}\n\nint __rtp_session_sendm_with_ts(RtpSession *session, mblk_t *mp, uint32_t packet_ts, uint32_t send_ts) {\n\treturn __rtp_session_sendm_with_ts_2(session, mp, packet_ts, send_ts, TRUE);\n}\n\n/**\n *\tSend the rtp datagram \\a packet to the destination set by rtp_session_set_remote_addr()\n *\twith timestamp \\a timestamp. For audio data, the timestamp is the number\n *\tof the first sample resulting of the data transmitted. See rfc1889 for details.\n *  The packet (\\a packet) is freed once it is sent.\n *\n *@param session a rtp session.\n *@param packet a rtp packet presented as a mblk_t.\n *@param timestamp the timestamp of the data to be sent.\n * @return the number of bytes sent over the network.\n **/\n\nint rtp_session_sendm_with_ts(RtpSession *session, mblk_t *packet, uint32_t timestamp) {\n\treturn __rtp_session_sendm_with_ts(session, packet, timestamp + session->send_ts_offset,\n\t                                   timestamp + session->send_ts_offset);\n}\n\n/**\n *\tSend a rtp datagram to the destination set by rtp_session_set_remote_addr() containing\n *\tthe data from \\a buffer with timestamp \\a userts. This is a high level function that uses\n *\trtp_session_create_packet() and rtp_session_sendm_with_ts() to send the data.\n *\n *@param session a rtp session.\n *@param buffer a buffer containing the data to be sent in a rtp packet.\n *@param len the length of the data buffer, in bytes.\n *@param userts\tthe timestamp of the data to be sent. Refer to the rfc to know what it is.\n *@return the number of bytes sent over the network.\n **/\nint rtp_session_send_with_ts(RtpSession *session, const uint8_t *buffer, int len, uint32_t userts) {\n\tmblk_t *m;\n\tint err;\n#ifdef USE_SENDMSG\n\t// message in two chained fragments: header and payload\n\tm = rtp_session_create_packet_header(session, 0);\n\tm->b_cont = rtp_package_packet((uint8_t *)buffer, len, NULL);\n#else\n\t// message in one block - continuous buffer\n\tm = rtp_session_create_packet_header(\n\t    session, len); // ask for len size to be allocated after the header so we can copy the payload into it\n\tmemcpy(m->b_wptr, buffer, len);\n\tm->b_wptr += len;\n#endif\n\terr = rtp_session_sendm_with_ts(session, m, userts);\n\treturn err;\n}\n\nstatic void payload_type_changed_notify(RtpSession *session, int paytype) {\n\tPayloadType *pt = rtp_profile_get_payload(session->rcv.profile, paytype);\n\tif (pt) {\n\t\tsession->rcv.pt = paytype;\n\t\trtp_signal_table_emit(&session->on_payload_type_changed);\n\t}\n}\n/**\n *\tTry to get an rtp packet presented as a mblk_t structure from the rtp session at a given sequence number.\n *\tThis function is very usefull for codec with Forward error correction capabilities\n *\n *\tThis function returns the entire packet (with header).\n *\n *\t *\n * @param session a rtp session.\n * @param sequence_number a sequence number.\n *\n * @return a rtp packet presented as a mblk_t, or NULL if not found.\n **/\n\nmblk_t *rtp_session_pick_with_cseq(RtpSession *session, const uint16_t sequence_number) {\n\tqueue_t *q = &session->rtp.rq;\n\tmblk_t *mb;\n\tfor (mb = qbegin(q); !qend(q, mb); mb = qnext(q, mb)) {\n\t\tif (rtp_get_seqnumber(mb) == sequence_number) {\n\t\t\treturn mb;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void check_for_seq_number_gap(RtpSession *session, rtp_header_t *rtp) {\n\tuint16_t pid;\n\tuint16_t i;\n\tuint16_t seq_number = rtp_header_get_seqnumber(rtp);\n\n\t/*don't check anything before first packet delivered*/\n\tif (session->flags & RTP_SESSION_FIRST_PACKET_DELIVERED &&\n\t    RTP_SEQ_IS_STRICTLY_GREATER_THAN(seq_number, session->rtp.rcv_last_seq + 1)) {\n\t\tuint16_t first_missed_seq = session->rtp.rcv_last_seq + 1;\n\t\tuint16_t diff = seq_number - first_missed_seq;\n\t\tpid = first_missed_seq;\n\t\tfor (i = 0; i <= (diff / 16); i++) {\n\t\t\tuint16_t seq;\n\t\t\tuint16_t blp = 0;\n\t\t\tfor (seq = pid + 1; (seq < seq_number) && ((seq - pid) < 16); seq++) {\n\t\t\t\tblp |= (1 << (seq - pid - 1));\n\t\t\t}\n\t\t\trtp_session_send_rtcp_fb_generic_nack(session, pid, blp);\n\t\t\tpid = seq;\n\t\t}\n\t}\n}\n\n/**\n * Detects and attempts to recover the missing rtp packets between the last one received in the \\a\n * session->rtp.rcv_last_seq and the newest one in the jitter buffer, using the Forward Error Correction (FEC)\n * algorithm. The missing packets are detected by analyzing the sequence of rtp sequence numbers. Each time a gap > 1 is\n * found, the function fec_stream_find_missing_packet is called and the FEC algorithm is applied. If the correction is\n * successful, the FEC function returns a non NULL recovered rtp packet on which the modifiers have been applied,\n * that is inserted into the jitter buffer.\n *\n * This function works only if the FEC is enabled.\n *\n *\n * @param session a rtp session.\n **/\nstatic void apply_fec_on_missing_packets(RtpSession *session) {\n\n\tuint16_t last_seq_num = session->rtp.rcv_last_seq;\n\tmblk_t *mp_newest = qlast(&session->rtp.rq);\n\n\tif (mp_newest != NULL) {\n\t\tuint16_t newest_seq_num = rtp_get_seqnumber(mp_newest);\n\t\tif (newest_seq_num - last_seq_num > (uint16_t)session->rtp.rq.q_mcount) {\n\n\t\t\tuint16_t ref_seq_num = last_seq_num;\n\t\t\tuint16_t next_seq_num = 0;\n\t\t\tuint16_t seq_num_diff = 0;\n\t\t\tfor (mblk_t *mp = qbegin(&session->rtp.rq); !qend(&session->rtp.rq, mp); mp = qnext(&session->rtp.rq, mp)) {\n\n\t\t\t\tif (mp != NULL) {\n\t\t\t\t\tuint16_t seq_num_missing = ref_seq_num + 1;\n\n\t\t\t\t\t/* detect missing packets */\n\t\t\t\t\tnext_seq_num = rtp_get_seqnumber(mp);\n\n\t\t\t\t\tif (next_seq_num < ref_seq_num) {\n\t\t\t\t\t\tortp_message(\"Wrong sequence numbers in jitter buffer: reference %u, next %u\", next_seq_num,\n\t\t\t\t\t\t             ref_seq_num);\n\t\t\t\t\t\tseq_num_diff = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tseq_num_diff = next_seq_num - ref_seq_num;\n\t\t\t\t\t\twhile (seq_num_diff > 1) {\n\n\t\t\t\t\t\t\tmblk_t *fec_mp = NULL;\n\t\t\t\t\t\t\tfec_mp = fec_stream_find_missing_packet(session->fec_stream, seq_num_missing);\n\n\t\t\t\t\t\t\tif (fec_mp != NULL) {\n\t\t\t\t\t\t\t\t/* inject recovered packet in jitter buffer */\n\t\t\t\t\t\t\t\trtp_putq(&session->rtp.rq, fec_mp);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tseq_num_missing++;\n\t\t\t\t\t\t\tseq_num_diff--;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tref_seq_num = next_seq_num;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n *\tTry to get a rtp packet presented as a mblk_t structure from the rtp session.\n *\tThe \\a user_ts parameter is relative to the first timestamp of the incoming stream. In other\n *\twords, the application does not have to know the first timestamp of the stream, it can\n *\tsimply call for the first time this function with \\a user_ts=0, and then incrementing it\n *\tas it want. The RtpSession takes care of synchronisation between the stream timestamp\n *\tand the user timestamp given here.\n *\n *\tThis function returns the entire packet (with header).\n *\n *\tThe behaviour of this function has changed since version 0.15.0. Previously the payload data could be\n *\taccessed using  mblk_t::b_cont::b_rptr field of the returned mblk_t.\n *\tThis is no more the case.\n *\tThe convenient way of accessing the payload data is to use rtp_get_payload() :\n *\t@code\n *\tunsigned char *payload;\n *\tint payload_size;\n *\tpayload_size=rtp_get_payload(mp,&payload);\n *\t@endcode\n *\tOR simply skip the header this way, the data is then comprised between mp->b_rptr and mp->b_wptr:\n *\t@code\n *\trtp_get_payload(mp,&mp->b_rptr);\n *\t@endcode\n *\n *\n * @param session a rtp session.\n * @param user_ts a timestamp.\n *\n * @return a rtp packet presented as a mblk_t.\n **/\n\nmblk_t *rtp_session_recvm_with_ts(RtpSession *session, uint32_t user_ts) {\n\tmblk_t *mp = NULL;\n\trtp_header_t *rtp = NULL;\n\tuint32_t ts;\n\tuint32_t packet_time;\n\tRtpScheduler *sched = session->sched;\n\tint rejected = 0;\n\tbool_t read_socket = TRUE;\n\n\t/* if we are scheduled, remember the scheduler time at which the application has\n\t * asked for its first timestamp */\n\n\tif (session->flags & RTP_SESSION_RECV_NOT_STARTED) {\n\t\tsession->rtp.rcv_query_ts_offset = user_ts;\n\t\t/* Set initial last_rcv_time to first recv time. */\n\t\tif ((session->flags & RTP_SESSION_SEND_NOT_STARTED) || session->mode == RTP_SESSION_RECVONLY) {\n\t\t\tbctbx_gettimeofday(&session->last_recv_time, NULL);\n\t\t}\n\t\tif (session->flags & RTP_SESSION_SCHEDULED) {\n\t\t\tsession->rtp.rcv_time_offset = sched->time_;\n\t\t\t// ortp_message(\"setting snd_time_offset=%i\",session->rtp.snd_time_offset);\n\t\t}\n\t\trtp_session_unset_flag(session, RTP_SESSION_RECV_NOT_STARTED);\n\t} else {\n\t\t/*prevent reading from the sockets when two\n\t\tconsecutives calls for a same timestamp*/\n\t\tif (user_ts == session->rtp.rcv_last_app_ts) read_socket = FALSE;\n\t}\n\tsession->rtp.rcv_last_app_ts = user_ts;\n\tif (read_socket) {\n\t\trtp_session_rtp_recv(session, user_ts);\n\t\trtp_session_rtcp_recv(session);\n\t}\n\n\tif (!session->transfer_mode) {\n\t\t/* check for telephone event first */\n\t\tmp = getq(&session->rtp.tev_rq);\n\t\tif (mp != NULL) {\n\t\t\tsize_t msgsize = msgdsize(mp);\n\t\t\tortp_global_stats.recv += msgsize;\n\t\t\tsession->stats.recv += msgsize;\n\t\t\trtp_signal_table_emit2(&session->on_telephone_event_packet, mp);\n\t\t\trtp_session_check_telephone_events(session, mp);\n\t\t\tfreemsg(mp);\n\t\t\tmp = NULL;\n\t\t}\n\t}\n\n\t/* then now try to return a media packet, if possible */\n\t/* first condition: if the session is starting, don't return anything\n\t * until the queue size reaches jitt_comp */\n\n\tif (session->flags & RTP_SESSION_RECV_SYNC) {\n\t\tqueue_t *q = &session->rtp.rq;\n\t\tif (qempty(q)) {\n\t\t\tortp_debug(\"Queue is empty.\");\n\t\t\tgoto end;\n\t\t}\n\t\trtp = (rtp_header_t *)qfirst(q)->b_rptr;\n\t\tsession->rtp.rcv_ts_offset = rtp_header_get_timestamp(rtp);\n\t\tsession->rtp.rcv_last_ret_ts = user_ts; /* just to have an init value */\n\t\tsession->rcv.ssrc = rtp_header_get_ssrc(rtp);\n\t\t/* delete the recv synchronisation flag */\n\t\trtp_session_unset_flag(session, RTP_SESSION_RECV_SYNC);\n\t}\n\n\t/* if FEC enabled: detect and recover missing packets in jitter buffer */\n\tif ((session->flags & RTP_SESSION_FIRST_PACKET_DELIVERED) && (session->fec_stream != NULL)) {\n\t\tapply_fec_on_missing_packets(session);\n\t}\n\n\t/*calculate the stream timestamp from the user timestamp */\n\tts = jitter_control_get_compensated_timestamp(&session->rtp.jittctl, user_ts);\n\tif (session->rtp.jittctl.params.enabled == TRUE) {\n\t\tif (session->permissive) {\n\t\t\tmp = rtp_peekq_permissive(&session->rtp.rq, ts, &rejected);\n\t\t} else {\n\t\t\tmp = rtp_peekq(&session->rtp.rq, ts, &rejected);\n\t\t}\n\t} else mp = peekq(&session->rtp.rq); /*no jitter buffer at all*/\n\n\tsession->stats.outoftime += rejected;\n\tortp_global_stats.outoftime += rejected;\n\tsession->rtcp_xr_stats.discarded_count += rejected;\n\nend:\n\n\tif (!qempty(&session->rtp.rq) && mp != NULL) remq(&session->rtp.rq, mp);\n\n\tif (mp != NULL) {\n\t\tsize_t msgsize = msgdsize(mp); /* evaluate how much bytes (including header) is received by app */\n\t\tuint32_t packet_ts;\n\t\tif ((session->flags & RTP_SESSION_FIRST_PACKET_DELIVERED) && (session->fec_stream != NULL)) {\n\t\t\t/* count missing rtp packets for FEC stats*/\n\t\t\tuint16_t seq_num = (int16_t)rtp_get_seqnumber(mp);\n\t\t\tif (seq_num > session->rtp.rcv_last_seq + 1) {\n\t\t\t\tint16_t seq_num_diff = seq_num - (int16_t)session->rtp.rcv_last_seq - 1;\n\t\t\t\tfec_stream_count_lost_packets(session->fec_stream, seq_num, seq_num_diff);\n\t\t\t}\n\t\t}\n\t\tortp_global_stats.recv += msgsize;\n\t\tsession->stats.recv += msgsize;\n\t\trtp = (rtp_header_t *)mp->b_rptr;\n\t\tpacket_ts = rtp_header_get_timestamp(rtp);\n\t\tortp_debug(\"Returning mp with ts=%i\", packet_ts);\n\t\t/* check for payload type changes */\n\t\tif (session->rcv.pt != rtp->paytype) {\n\t\t\tpayload_type_changed_notify(session, rtp->paytype);\n\t\t}\n\n\t\tif ((rtp_session_avpf_enabled(session) == TRUE) &&\n\t\t    (rtp_session_avpf_feature_enabled(session, ORTP_AVPF_FEATURE_GENERIC_NACK) == TRUE) &&\n\t\t    (rtp_session_avpf_feature_enabled(session, ORTP_AVPF_FEATURE_IMMEDIATE_NACK) == FALSE)) {\n\t\t\t/*\n\t\t\t * If immediate nack is disabled then we check for missing packets here instead of\n\t\t\t * rtp_session_rtp_parse\n\t\t\t */\n\t\t\tcheck_for_seq_number_gap(session, rtp);\n\t\t}\n\n\t\t/* update the packet's timestamp so that it corrected by the\n\t\tadaptive jitter buffer mechanism */\n\t\tif (session->rtp.jittctl.params.enabled && session->rtp.jittctl.params.adaptive) {\n\t\t\tuint32_t changed_ts;\n\t\t\t/* only update correction offset between packets of different\n\t\t\ttimestamps*/\n\t\t\tif (packet_ts != session->rtp.rcv_last_ts) jitter_control_update_corrective_slide(&session->rtp.jittctl);\n\t\t\tchanged_ts = packet_ts + session->rtp.jittctl.corrective_slide;\n\t\t\trtp_header_set_timestamp(rtp, changed_ts);\n\t\t\t/*ortp_debug(\"Returned packet has timestamp %u, with clock slide compensated it is\n\t\t\t * %u\",packet_ts,rtp_header_get_timestamp(rtp));*/\n\t\t}\n\t\tif ((session->flags & RTP_SESSION_FIRST_PACKET_DELIVERED)) {\n\n\t\t\tint diff = rtp_header_get_seqnumber(rtp) - session->rtp.rcv_last_seq;\n\t\t\tif (diff >= 3) {\n\t\t\t\tOrtpEvent *event = NULL;\n\t\t\t\tOrtpEventData *event_data = NULL;\n\t\t\t\tevent = ortp_event_new(ORTP_EVENT_BURST_OCCURED);\n\t\t\t\tevent_data = ortp_event_get_data(event);\n\t\t\t\tevent_data->info.sequence_number_diff = diff;\n\t\t\t\trtp_session_dispatch_event(session, event);\n\t\t\t}\n\t\t}\n\t\tsession->rtp.rcv_last_ts = packet_ts;\n\t\tsession->rtp.rcv_last_seq = rtp_header_get_seqnumber(rtp);\n\t\tif (!(session->flags & RTP_SESSION_FIRST_PACKET_DELIVERED)) {\n\t\t\trtp_session_set_flag(session, RTP_SESSION_FIRST_PACKET_DELIVERED);\n\t\t}\n\t\tortp_mutex_lock(&session->main_mutex);\n\t\tif (session->transfer_mode &&\n\t\t    session->bundle) { /* in transfer mode, we must delete possible bundle header extension */\n\t\t\tint midId = rtp_bundle_get_mid_extension_id(session->bundle);\n\t\t\trtp_delete_extension_header(mp, midId != -1 ? midId : RTP_EXTENSION_MID);\n\t\t}\n\t\tortp_mutex_unlock(&session->main_mutex);\n\t} else {\n\t\tortp_debug(\"No mp for timestamp queried\");\n\t}\n\tif (session->fec_stream != NULL) {\n\t\tfec_stream_receive_repair_packet(session->fec_stream, user_ts);\n\t}\n\trtp_session_rtcp_process_recv(session);\n\n\tif (session->flags & RTP_SESSION_SCHEDULED) {\n\t\t/* if we are in blocking mode, then suspend the calling process until timestamp\n\t\t * wanted expires */\n\t\t/* but we must not block the process if the timestamp wanted by the application is older\n\t\t * than current time */\n\t\twait_point_lock(&session->rcv.wp);\n\t\tpacket_time =\n\t\t    rtp_session_ts_to_time(session, user_ts - session->rtp.rcv_query_ts_offset) + session->rtp.rcv_time_offset;\n\t\tortp_debug(\"rtp_session_recvm_with_ts: packet_time=%i, time=%i\", packet_time, sched->time_);\n\n\t\tif (TIME_IS_STRICTLY_NEWER_THAN(packet_time, sched->time_)) {\n\t\t\twait_point_wakeup_at(&session->rcv.wp, packet_time, (session->flags & RTP_SESSION_BLOCKING_MODE) != 0);\n\t\t\tsession_set_clr(&sched->r_sessions, session);\n\t\t} else session_set_set(&sched->r_sessions, session); /*to unblock _select() immediately */\n\t\twait_point_unlock(&session->rcv.wp);\n\t}\n\n\treturn mp;\n}\n\n/**\n *\tNOTE: use of this function is discouraged when sending payloads other than\n *\tpcm/pcmu/pcma/adpcm types.\n *\trtp_session_recvm_with_ts() does better job.\n *\n *\tTries to read the bytes of the incoming rtp stream related to timestamp ts. In case\n *\twhere the user supplied buffer \\a buffer is not large enough to get all the data\n *\trelated to timestamp ts, then *( have_more) is set to 1 to indicate that the application\n *\tshould recall the function with the same timestamp to get more data.\n *\n *  When the rtp session is scheduled (see rtp_session_set_scheduling_mode() ), and the\n *\tblocking mode is on (see rtp_session_set_blocking_mode() ), then the calling thread\n *\tis suspended until the timestamp given as argument expires, whatever a received packet\n *\tfits the query or not.\n *\n *\tImportant note: it is clear that the application cannot know the timestamp of the first\n *\tpacket of the incoming stream, because it can be random. The \\a ts timestamp given to the\n *\tfunction is used relatively to first timestamp of the stream. In simple words, 0 is a good\n *\tvalue to start calling this function.\n *\n *\tThis function internally calls rtp_session_recvm_with_ts() to get a rtp packet. The content\n *\tof this packet is then copied into the user supplied buffer in an intelligent manner:\n *\tthe function takes care of the size of the supplied buffer and the timestamp given in\n *\targument. Using this function it is possible to read continous audio data (e.g. pcma,pcmu...)\n *\twith for example a standart buffer of size of 160 with timestamp incrementing by 160 while the incoming\n *\tstream has a different packet size.\n *\n *Returns: if a packet was availlable with the corresponding timestamp supplied in argument\n *\tthen the number of bytes written in the user supplied buffer is returned. If no packets\n *\tare availlable, either because the sender has not started to send the stream, or either\n *\tbecause silence packet are not transmitted, or either because the packet was lost during\n *\tnetwork transport, then the function returns zero.\n *@param session a rtp session.\n *@param buffer a user supplied buffer to write the data.\n *@param len the length in bytes of the user supplied buffer.\n *@param ts the timestamp wanted.\n *@param have_more the address of an integer to indicate if more data is availlable for the given timestamp.\n *\n **/\nint rtp_session_recv_with_ts(RtpSession *session, uint8_t *buffer, int len, uint32_t ts, int *have_more) {\n\tmblk_t *mp = NULL;\n\tint plen, blen = 0;\n\t*have_more = 0;\n\twhile (1) {\n\t\tif (session->pending) {\n\t\t\tmp = session->pending;\n\t\t\tsession->pending = NULL;\n\t\t} else {\n\t\t\tmp = rtp_session_recvm_with_ts(session, ts);\n\t\t\tif (mp != NULL) rtp_get_payload(mp, &mp->b_rptr);\n\t\t}\n\t\tif (mp) {\n\t\t\tplen = (int)(mp->b_wptr - mp->b_rptr);\n\t\t\tif (plen <= len) {\n\t\t\t\tmemcpy(buffer, mp->b_rptr, plen);\n\t\t\t\tbuffer += plen;\n\t\t\t\tblen += plen;\n\t\t\t\tlen -= plen;\n\t\t\t\tfreemsg(mp);\n\t\t\t\tmp = NULL;\n\t\t\t} else {\n\t\t\t\tmemcpy(buffer, mp->b_rptr, len);\n\t\t\t\tmp->b_rptr += len;\n\t\t\t\tbuffer += len;\n\t\t\t\tblen += len;\n\t\t\t\tlen = 0;\n\t\t\t\tsession->pending = mp;\n\t\t\t\t*have_more = 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else break;\n\t}\n\treturn blen;\n}\n/**\n *\tWhen the rtp session is scheduled and has started to send packets, this function\n *\tcomputes the timestamp that matches to the present time. Using this function can be\n *\tusefull when sending discontinuous streams. Some time can be elapsed between the end\n *\tof a stream burst and the begin of a new stream burst, and the application may be not\n *\tnot aware of this elapsed time. In order to get a valid (current) timestamp to pass to\n *\t#rtp_session_send_with_ts() or #rtp_session_sendm_with_ts(), the application may\n *\tuse rtp_session_get_current_send_ts().\n *\n * @param session a rtp session.\n * @return the current send timestamp for the rtp session.\n **/\nuint32_t rtp_session_get_current_send_ts(RtpSession *session) {\n\tuint32_t userts;\n\tuint32_t session_time;\n\tRtpScheduler *sched = session->sched;\n\tPayloadType *payload;\n\tpayload = rtp_profile_get_payload(session->snd.profile, session->snd.pt);\n\treturn_val_if_fail(payload != NULL, 0);\n\tif ((session->flags & RTP_SESSION_SCHEDULED) == 0) {\n\t\tortp_warning(\"can't guess current timestamp because session is not scheduled.\");\n\t\treturn 0;\n\t}\n\tsession_time = sched->time_ - session->rtp.snd_time_offset;\n\tuserts = (uint32_t)(((double)(session_time) * (double)payload->clock_rate) / 1000.0) + session->rtp.snd_ts_offset;\n\treturn userts;\n}\n\n/**\n * Same thing as rtp_session_get_current_send_ts() except that it's for an incoming stream.\n * Works only on scheduled mode.\n *\n * @param session a rtp session.\n * @return the theoritical that would have to be receive now.\n *\n **/\nuint32_t rtp_session_get_current_recv_ts(RtpSession *session) {\n\tuint32_t userts;\n\tuint32_t session_time;\n\tRtpScheduler *sched = ortp_get_scheduler();\n\tPayloadType *payload;\n\tpayload = rtp_profile_get_payload(session->rcv.profile, session->rcv.pt);\n\treturn_val_if_fail(payload != NULL, 0);\n\tif ((session->flags & RTP_SESSION_SCHEDULED) == 0) {\n\t\tortp_warning(\"can't guess current timestamp because session is not scheduled.\");\n\t\treturn 0;\n\t}\n\tsession_time = sched->time_ - session->rtp.rcv_time_offset;\n\tuserts = (uint32_t)(((double)(session_time) * (double)payload->clock_rate) / 1000.0) + session->rtp.rcv_ts_offset;\n\treturn userts;\n}\n\n/**\n * oRTP has the possibility to inform the application through a callback registered\n * with rtp_session_signal_connect about crazy incoming RTP stream that jumps from\n * a timestamp N to N+some_crazy_value. This lets the opportunity for the application\n * to reset the session in order to resynchronize, or any other action like stopping the call\n * and reporting an error.\n * @param session the rtp session\n * @param milisecs a time interval in miliseconds\n *\n **/\nvoid rtp_session_set_time_jump_limit(RtpSession *session, int milisecs) {\n\tuint32_t ts;\n\tsession->rtp.time_jump = milisecs;\n\tts = rtp_session_time_to_ts(session, milisecs);\n\tif (ts == 0) session->rtp.ts_jump = (uint32_t)1 << (uint32_t)31; /* do not detect ts jump */\n\telse session->rtp.ts_jump = ts;\n}\n\nvoid _rtp_session_release_sockets(RtpSession *session, bool_t release_transports) {\n\n\tif (release_transports) {\n\t\tif (session->rtp.gs.tr) {\n\t\t\tif (session->rtp.gs.tr->t_close) session->rtp.gs.tr->t_close(session->rtp.gs.tr);\n\t\t\tsession->rtp.gs.tr->t_destroy(session->rtp.gs.tr);\n\t\t}\n\t\tsession->rtp.gs.tr = 0;\n\n\t\tif (session->rtcp.gs.tr) {\n\t\t\tif (session->rtcp.gs.tr->t_close) session->rtcp.gs.tr->t_close(session->rtcp.gs.tr);\n\t\t\tsession->rtcp.gs.tr->t_destroy(session->rtcp.gs.tr);\n\t\t}\n\t\tsession->rtcp.gs.tr = 0;\n\t}\n\n\tif (session->rtp.gs.socket != (ortp_socket_t)-1) close_socket(session->rtp.gs.socket);\n\tif (session->rtcp.gs.socket != (ortp_socket_t)-1) close_socket(session->rtcp.gs.socket);\n\tsession->rtp.gs.socket = -1;\n\tsession->rtcp.gs.socket = -1;\n\n\t/* don't discard remote addresses, then can be preserved for next use.\n\tsession->rtp.gs.rem_addrlen=0;\n\tsession->rtcp.gs.rem_addrlen=0;\n\t*/\n}\n\n/**\n * Closes the rtp and rtcp sockets, and associated RtpTransport.\n **/\nvoid rtp_session_release_sockets(RtpSession *session) {\n\t_rtp_session_release_sockets(session, TRUE);\n}\n\nortp_socket_t rtp_session_get_rtp_socket(const RtpSession *session) {\n\treturn rtp_session_using_transport(session, rtp) ? (session->rtp.gs.tr->t_getsocket)(session->rtp.gs.tr)\n\t                                                 : session->rtp.gs.socket;\n}\n\nortp_socket_t rtp_session_get_rtcp_socket(const RtpSession *session) {\n\treturn rtp_session_using_transport(session, rtcp) ? (session->rtcp.gs.tr->t_getsocket)(session->rtcp.gs.tr)\n\t                                                  : session->rtcp.gs.socket;\n}\n\n/**\n * Register an event queue.\n * An application can use an event queue to get informed about various RTP events.\n **/\nvoid rtp_session_register_event_queue(RtpSession *session, OrtpEvQueue *q) {\n\tsession->eventqs = o_list_append(session->eventqs, q);\n}\n\nvoid rtp_session_unregister_event_queue(RtpSession *session, OrtpEvQueue *q) {\n\tsession->eventqs = o_list_remove(session->eventqs, q);\n}\n\nvoid rtp_session_unregister_event_queues(RtpSession *session) {\n\to_list_free(session->eventqs);\n\tsession->eventqs = NULL;\n}\n\nvoid rtp_session_dispatch_event(RtpSession *session, OrtpEvent *ev) {\n\tOList *it;\n\tint i;\n\tfor (i = 0, it = session->eventqs; it != NULL; it = it->next, ++i) {\n\t\tortp_ev_queue_put((OrtpEvQueue *)it->data, ortp_event_dup(ev));\n\t}\n\tortp_event_destroy(ev);\n}\n\nvoid ortp_stream_clear_aux_addresses(OrtpStream *os) {\n\tOList *elem;\n\tfor (elem = os->aux_destinations; elem != NULL; elem = elem->next) {\n\t\tOrtpAddress *addr = (OrtpAddress *)elem->data;\n\t\tortp_free(addr);\n\t}\n\tos->aux_destinations = o_list_free(os->aux_destinations);\n}\n\nstatic void ortp_stream_init(OrtpStream *os) {\n\tqinit(&os->bundleq);\n\tortp_mutex_init(&os->bundleq_lock, NULL);\n}\n\nstatic void ortp_stream_uninit(OrtpStream *os) {\n\tflushq(&os->bundleq, FLUSHALL);\n\tortp_mutex_destroy(&os->bundleq_lock);\n\tortp_stream_clear_aux_addresses(os);\n}\n\nvoid rtp_session_uninit(RtpSession *session) {\n\tRtpTransport *rtp_meta_transport = NULL;\n\tRtpTransport *rtcp_meta_transport = NULL;\n\n\t/* Stop and destroy network simulator first, as its thread must be stopped before we free anything else in the\n\t * RtpSession. */\n\tif (session->net_sim_ctx) {\n\t\tortp_network_simulator_stop_thread(session->net_sim_ctx);\n\t\tortp_network_simulator_destroy(session->net_sim_ctx);\n\t}\n\n\t/* If rtp async thread is running stop it and wait fot it to finish */\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\tif (session->rtp.is_win_thread_running) {\n\t\tsession->rtp.is_win_thread_running = FALSE;\n\t\tortp_thread_join(session->rtp.win_t, NULL);\n\t}\n\tortp_mutex_destroy(&session->rtp.winthread_lock);\n\tortp_mutex_destroy(&session->rtp.winrq_lock);\n#endif\n\n\t/* first of all remove the session from the scheduler */\n\tif (session->flags & RTP_SESSION_SCHEDULED) {\n\t\trtp_scheduler_remove_session(session->sched, session);\n\t}\n\n\t/*flush all queues */\n\tflushq(&session->rtp.rq, FLUSHALL);\n\tflushq(&session->rtp.tev_rq, FLUSHALL);\n\tflushq(&session->rtp.winrq, FLUSHALL);\n\n\tif (session->eventqs != NULL) o_list_free(session->eventqs);\n\t/* close sockets */\n\trtp_session_release_sockets(session);\n\n\twait_point_uninit(&session->snd.wp);\n\twait_point_uninit(&session->rcv.wp);\n\tif (session->current_tev != NULL) freemsg(session->current_tev);\n\tortp_stream_uninit(&session->rtp.gs);\n\tortp_stream_uninit(&session->rtcp.gs);\n\tbctbx_list_free_with_data(session->recv_addr_map, (bctbx_list_free_func)bctbx_free);\n\n\tsession->signal_tables = o_list_free_with_data(session->signal_tables, (o_list_free_func)rtp_signal_table_uninit);\n\n\tif (session->rtp.congdetect) {\n\t\tortp_congestion_detector_destroy(session->rtp.congdetect);\n\t}\n\n\tif (session->rtp.video_bw_estimator) {\n\t\tortp_video_bandwidth_estimator_destroy(session->rtp.video_bw_estimator);\n\t}\n\n\tif (session->rtp.audio_bw_estimator) {\n\t\tortp_audio_bandwidth_estimator_destroy(session->rtp.audio_bw_estimator);\n\t}\n\tortp_bandwidth_measurer_destroy(session->rtp.gs.recv_bw_estimator);\n\tortp_bandwidth_measurer_destroy(session->rtp.gs.recv_average_bw_estimator);\n\tortp_bandwidth_measurer_destroy(session->rtcp.gs.recv_bw_estimator);\n\tortp_bandwidth_measurer_destroy(session->rtcp.gs.recv_average_bw_estimator);\n\n\tortp_bandwidth_measurer_destroy(session->rtp.gs.send_bw_estimator);\n\tortp_bandwidth_measurer_destroy(session->rtp.gs.send_average_bw_estimator);\n\tortp_bandwidth_measurer_destroy(session->rtcp.gs.send_bw_estimator);\n\tortp_bandwidth_measurer_destroy(session->rtcp.gs.send_average_bw_estimator);\n\n\trtp_session_get_transports(session, &rtp_meta_transport, &rtcp_meta_transport);\n\tif (rtp_meta_transport) meta_rtp_transport_destroy(rtp_meta_transport);\n\tif (rtcp_meta_transport) meta_rtp_transport_destroy(rtcp_meta_transport);\n\n#if (_WIN32_WINNT >= 0x0600) && defined(ORTP_WINDOWS_DESKTOP)\n#if !defined(ENABLE_MICROSOFT_STORE_APP) && !defined(ORTP_WINDOWS_UWP)\n\tif (session->rtp.QoSFlowID != 0) {\n\t\tortp_message(\"check OS support for qwave.lib\");\n\t\tif (IsWindowsVistaOrGreater()) {\n\t\t\tBOOL QoSResult;\n\t\t\tQoSResult = QOSRemoveSocketFromFlow(session->rtp.QoSHandle, 0, session->rtp.QoSFlowID, 0);\n\t\t\tif (QoSResult != TRUE) {\n\t\t\t\tortp_error(\"QOSRemoveSocketFromFlow failed to end a flow with error %d\", GetLastError());\n\t\t\t}\n\t\t\tsession->rtp.QoSFlowID = 0;\n\t\t}\n\t}\n\n\tif (session->rtp.QoSHandle != NULL) {\n\t\tQOSCloseHandle(session->rtp.QoSHandle);\n\t\tsession->rtp.QoSHandle = NULL;\n\t}\n#endif // ENABLE_MICROSOFT_STORE_APP\n#endif\n\n\tif (session->rtcp.tmmbr_info.sent) freemsg(session->rtcp.tmmbr_info.sent);\n\tif (session->rtcp.tmmbr_info.received) freemsg(session->rtcp.tmmbr_info.received);\n\tif (session->rtcp.goog_remb_info.sent) freemsg(session->rtcp.goog_remb_info.sent);\n\tif (session->rtcp.send_algo.fb_packets) freemsg(session->rtcp.send_algo.fb_packets);\n\tortp_mutex_destroy(&session->main_mutex);\n\tif (session->recv_block_cache) freemsg(session->recv_block_cache);\n\n\tflushq(&session->contributing_sources, 0);\n\trtcp_sdes_items_uninit(&session->sdes_items);\n}\n\n/**\n * Sets the number of packets containing a new SSRC that will trigger the\n * \"ssrc_changed\" callback.\n **/\nvoid rtp_session_set_ssrc_changed_threshold(RtpSession *session, int numpackets) {\n\tsession->rtp.ssrc_changed_thres = numpackets;\n}\n\n/**\n * Resynchronize to the incoming RTP streams.\n * This can be useful to handle discontinuous timestamps.\n * For example, call this function from the timestamp_jump signal handler.\n * @param session the rtp session\n **/\nvoid rtp_session_resync(RtpSession *session) {\n\tflushq(&session->rtp.rq, FLUSHALL);\n\trtp_session_set_flag(session, RTP_SESSION_RECV_SYNC);\n\trtp_session_unset_flag(session, RTP_SESSION_FIRST_PACKET_DELIVERED);\n\trtp_session_init_jitter_buffer(session);\n\tif (session->rtp.congdetect) ortp_congestion_detector_reset(session->rtp.congdetect);\n\tif (session->rtp.video_bw_estimator) ortp_video_bandwidth_estimator_reset(session->rtp.video_bw_estimator);\n\tif (session->fec_stream) fec_stream_reset_cluster(session->fec_stream);\n\n\t/* Since multiple streams might share the same session (fixed RTCP port for example),\n\tRTCP values might be erroneous (number of packets received is computed\n\tover all streams, ...). There should be only one stream per RTP session*/\n\tsession->rtp.rcv_last_seq = 0;\n\tsession->rtp.snd_last_nack = 0;\n\tsession->rtp.hwrcv_extseq = 0;\n\tsession->rtp.hwrcv_since_last_SR = 0;\n\tsession->rtp.hwrcv_seq_at_last_SR = 0;\n\trtp_session_unset_flag(session, RTP_SESSION_RECV_SEQ_INIT);\n}\n\n/**\n * Reset the session: local and remote addresses are kept. It resets timestamp, sequence\n * number, and calls rtp_session_resync().\n *\n * @param session a rtp session.\n **/\nvoid rtp_session_reset(RtpSession *session) {\n\trtp_session_set_flag(session, RTP_SESSION_RECV_NOT_STARTED);\n\trtp_session_set_flag(session, RTP_SESSION_SEND_NOT_STARTED);\n\t// session->ssrc=0;\n\tsession->rtp.snd_time_offset = 0;\n\tsession->rtp.snd_ts_offset = 0;\n\tsession->rtp.snd_rand_offset = 0;\n\tsession->rtp.snd_last_ts = 0;\n\tsession->rtp.rcv_time_offset = 0;\n\tsession->rtp.rcv_ts_offset = 0;\n\tsession->rtp.rcv_query_ts_offset = 0;\n\tsession->rtp.rcv_last_ts = 0;\n\tsession->rtp.rcv_last_app_ts = 0;\n\tsession->rtp.hwrcv_extseq = 0;\n\tsession->rtp.hwrcv_since_last_SR = 0;\n\tsession->rtp.snd_seq = 0;\n\tsession->rtp.sent_payload_bytes = 0;\n\trtp_session_clear_send_error_code(session);\n\trtp_session_clear_recv_error_code(session);\n\trtp_stats_reset(&session->stats);\n\trtp_session_resync(session);\n\tsession->ssrc_set = FALSE;\n}\n\n/**\n * Retrieve the session's statistics.\n **/\nconst rtp_stats_t *rtp_session_get_stats(const RtpSession *session) {\n\treturn &session->stats;\n}\n\n/**\n * Retrieves the session's jitter specific statistics.\n **/\nconst jitter_stats_t *rtp_session_get_jitter_stats(const RtpSession *session) {\n\treturn &session->rtp.jitter_stats;\n}\n\n/**\n * @brief For <b>test purpose only</b>, sets a constant lost packet value within <b>all</b> RTCP output packets.@n\n *\n * The SR or RR RTCP packet contain a lost packet field. After this procedure is called, the lost packet field will be\n *set to a constant value in all output SR or RR packets. This parameter will overridden the actual number of lost\n *packets in the input RTP stream that the RTCP stack had previously processed.\n * @param s : the rtp session.\n * @param value : the lost packets test vector value.\n **/\nvoid rtp_session_rtcp_set_lost_packet_value(struct _RtpSession *s, const int value) {\n\ts->lost_packets_test_vector = value;\n\ts->flags |= RTCP_OVERRIDE_LOST_PACKETS;\n}\n\n/**\n * @brief For <b>test purpose only</b>, sets a constant interarrival_jitter value within <b>all</b> RTCP output\n *packets.@n\n *\n * The SR or RR RTCP packet contain an interarrival jitter field. After this procedure is called, the interarrival\n *jitter field will be set to a constant value in all output SR or RR packets. This parameter will overridden the actual\n *interarrival jitter value that was processed by the RTCP stack.\n * @param s : the rtp session.\n * @param value : the interarrival jitter test vector value.\n **/\nvoid rtp_session_rtcp_set_jitter_value(struct _RtpSession *s, const unsigned int value) {\n\ts->interarrival_jitter_test_vector = value;\n\ts->flags |= RTCP_OVERRIDE_JITTER;\n}\n\n/**\n * @brief For <b>test purpose only</b>, simulates a constant RTT (Round Trip Time) value by setting the LSR field within\n *<b>all</b> returned RTCP output packets.@n\n *\n * The RTT processing involves two RTCP packets exchanged between two different devices.@n\n * In a <b>normal</b> operation the device 1 issues a SR packets at time T0, hence this packet has a timestamp field set\n *to T0. The LSR and DLSR fiels of that packet are not considered here. This packet is received by the Device 2 at T1.\n * In response, the Device 2 issues another SR or RR packets at T2 with the following fields;\n * - a timestamp set to T2.\n * - a LSR (Last SR packet timestamp) field set to T0 ( this value has been extracted from the first packet).\n * - a DLSR (Delay since Last SR packet) field set to (T2 - T1).\n * .\n * This packet is received by The Device 1 at T3. So the Device 1 is now able to process the RTT using the formula :\n * RTT = T3 - LSR - DLSR = (T1 - T0) - (T3 - T2).@n\n * This way of processing is described in par. 6.4 of the RFC3550 standard.\n *\n * In the <b>test</b> mode that is enabled by this procedure, the RTCP stack is considered as beeing part of the\n *device 2. For setting the RTT to a constant RTT0 value, the Device 2 artificially sets the LSR field of the second\n *packet to (T1 - RTT0), instead of T0 in normal mode. The two other fields (timestamp and DLSR) are set as in the\n *normal mode. So the Device 1 will process : RTT = T3 - LSR - DLSR = RTT0 + (T3 - T2) that is near to RTT0 is T3 - T2\n *is small enough.\n * @note It is impossible to actually make the mesured RTT strictly equal to RTT0, as the packet trip time (T3 - T2) is\n *unknown when this packet is issued by the Device 2.\n * @param s : the rtp session.\n * @param value : The desired RTT test vector value (RTT0).\n **/\nvoid rtp_session_rtcp_set_delay_value(struct _RtpSession *s, const unsigned int value) {\n\ts->delay_test_vector = value;\n\ts->flags |= RTCP_OVERRIDE_DELAY;\n}\n\nvoid rtp_session_reset_stats(RtpSession *session) {\n\tmemset(&session->stats, 0, sizeof(rtp_stats_t));\n}\n\n/**\n * Stores some application specific data into the session, so that it is easy to retrieve it from the signal callbacks\n *using rtp_session_get_data().\n * @param session a rtp session\n * @param data an opaque pointer to be stored in the session\n **/\n\nvoid rtp_session_set_data(RtpSession *session, void *data) {\n\tsession->user_data = data;\n}\n\n/**\n * @param session a rtp session\n * @return the void pointer previously set using rtp_session_set_data()\n **/\nvoid *rtp_session_get_data(const RtpSession *session) {\n\treturn session->user_data;\n}\n\n/**\n * Enable or disable the \"rtp symmetric\" hack which consists of the following:\n * after the first packet is received, the source address of the packet\n * is set to be the destination address for all next packets.\n * This is useful to pass-through firewalls.\n * @param session a rtp session\n * @param yesno a boolean to enable or disable the feature\n *\n **/\nvoid rtp_session_set_symmetric_rtp(RtpSession *session, bool_t yesno) {\n\tsession->symmetric_rtp = yesno;\n}\n\nvoid rtp_session_enable_rtcp_mux(RtpSession *session, bool_t yesno) {\n\tsession->rtcp_mux = yesno;\n}\n\nbool_t rtp_session_rtcp_mux_enabled(RtpSession *session) {\n\treturn session->rtcp_mux;\n}\n\n/**\n *\tIf yesno is TRUE, thus a connect() syscall is done on the socket to\n *\tthe destination address set by rtp_session_set_remote_addr(), or\n *\tif the session does symmetric rtp (see rtp_session_set_symmetric_rtp())\n *\ta the connect() is done to the source address of the first packet received.\n *\tConnecting a socket has effect of rejecting all incoming packets that\n *\tdon't come from the address specified in connect().\n *\tIt also makes ICMP errors (such as connection refused) available to the\n *\tapplication.\n *\t@param session a rtp session\n *\t@param yesno a boolean to enable or disable the feature\n *\n **/\nvoid rtp_session_set_connected_mode(RtpSession *session, bool_t yesno) {\n\tsession->use_connect = yesno;\n}\n\n/**\n * Get last computed recv bandwidth.\n **/\nfloat rtp_session_get_recv_bandwidth(RtpSession *session) {\n\treturn ortp_bandwidth_measurer_get_bandwdith(session->rtp.gs.recv_bw_estimator) +\n\t       ortp_bandwidth_measurer_get_bandwdith(session->rtcp.gs.recv_bw_estimator);\n}\n\nfloat rtp_session_get_recv_bandwidth_smooth(RtpSession *session) {\n\treturn ortp_bandwidth_measurer_get_bandwdith(session->rtp.gs.recv_average_bw_estimator) +\n\t       ortp_bandwidth_measurer_get_bandwdith(session->rtcp.gs.recv_average_bw_estimator);\n}\n\n/**\n * Get last computed send bandwidth.\n **/\nfloat rtp_session_get_send_bandwidth(RtpSession *session) {\n\treturn ortp_bandwidth_measurer_get_bandwdith(session->rtp.gs.send_bw_estimator) +\n\t       ortp_bandwidth_measurer_get_bandwdith(session->rtcp.gs.send_bw_estimator);\n}\n\nfloat rtp_session_get_send_bandwidth_smooth(RtpSession *session) {\n\treturn ortp_bandwidth_measurer_get_bandwdith(session->rtp.gs.send_average_bw_estimator) +\n\t       ortp_bandwidth_measurer_get_bandwdith(session->rtcp.gs.send_average_bw_estimator);\n}\n\nfloat rtp_session_get_rtp_recv_bandwidth(RtpSession *session) {\n\treturn ortp_bandwidth_measurer_get_bandwdith(session->rtp.gs.recv_bw_estimator);\n}\n\nfloat rtp_session_get_rtp_send_bandwidth(RtpSession *session) {\n\treturn ortp_bandwidth_measurer_get_bandwdith(session->rtp.gs.send_bw_estimator);\n}\n\nfloat rtp_session_get_rtcp_recv_bandwidth(RtpSession *session) {\n\treturn ortp_bandwidth_measurer_get_bandwdith(session->rtcp.gs.recv_bw_estimator);\n}\n\nfloat rtp_session_get_rtcp_send_bandwidth(RtpSession *session) {\n\treturn ortp_bandwidth_measurer_get_bandwdith(session->rtcp.gs.send_bw_estimator);\n}\n\nint rtp_session_get_last_send_error_code(RtpSession *session) {\n\treturn session->rtp.send_errno;\n}\n\nvoid rtp_session_clear_send_error_code(RtpSession *session) {\n\tsession->rtp.send_errno = 0;\n}\n\nint rtp_session_get_last_recv_error_code(RtpSession *session) {\n\treturn session->rtp.recv_errno;\n}\n\nvoid rtp_session_clear_recv_error_code(RtpSession *session) {\n\tsession->rtp.send_errno = 0;\n}\n\n/**\n * Returns the last known round trip propagation delay.\n *\n * This value is known after successful RTCP SR or RR exchanged between a sender and a receiver.\n * oRTP automatically takes care of sending SR or RR packets.\n * You might want to call this function when you receive an RTCP event (see rtp_session_register_event_queue() ).\n * This value might not be known: at the beginning when no RTCP packets have been exchanged yet, or simply because the\n * rtcp channel is broken due to firewall problematics, or because the remote implementation does not support RTCP.\n *\n * @return the round trip propagation time in seconds if known, -1 if unknown.\n **/\nfloat rtp_session_get_round_trip_propagation(RtpSession *session) {\n\treturn session->rtt;\n}\n\n/**\n * Destroys a rtp session.\n * All memory allocated for the RtpSession is freed.\n *\n * @param session a rtp session.\n **/\nvoid rtp_session_destroy(RtpSession *session) {\n\trtp_session_uninit(session);\n\tortp_free(session);\n}\n\nvoid rtp_session_make_time_distorsion(RtpSession *session, int milisec) {\n\tsession->rtp.snd_time_offset += milisec;\n}\n\n/* packet api */\nvoid rtp_header_add_csrc(rtp_header_t *hdr, uint32_t csrc) {\n\thdr->csrc[hdr->cc] = htonl(csrc);\n\thdr->cc++;\n}\n\nvoid rtp_add_csrc(mblk_t *mp, uint32_t csrc) {\n\trtp_header_add_csrc((rtp_header_t *)mp->b_rptr, csrc);\n}\n\n/**\n * Get a pointer to the beginning of the payload data of the RTP packet.\n * @param packet a RTP packet represented as a mblk_t\n * @param start a pointer to the beginning of the payload data, pointing inside the packet.\n * @return the length of the payload data.\n **/\nint rtp_get_payload(mblk_t *packet, unsigned char **start) {\n\tunsigned char *tmp;\n\tint header_len = RTP_FIXED_HEADER_SIZE + (rtp_get_cc(packet) * 4);\n\ttmp = packet->b_rptr + header_len; // tmp point at the end of header\n\n\tif (rtp_get_extbit(packet)) { // Do we have extension header ?\n\t\tint extsize = rtp_get_extheader(packet, NULL, NULL);\n\t\tif (extsize >= 0) {\n\t\t\theader_len += 4 + extsize;\n\t\t\ttmp += 4 + extsize; // tmp point after the header + extension\n\t\t}\n\t}\n\n\tif (tmp >= packet->b_wptr) { // pointing after the header(+ ext) gets us out of the first block. We shall have a\n\t\t                         // fragmented message block\n\t\tif (packet->b_cont != NULL) {\n\t\t\ttmp = packet->b_cont->b_rptr + (header_len - (packet->b_wptr - packet->b_rptr));\n\t\t\tif (tmp <= packet->b_cont->b_wptr) {\n\t\t\t\t*start = tmp;\n\t\t\t\treturn (int)(packet->b_cont->b_wptr - tmp);\n\t\t\t}\n\t\t}\n\t\tortp_warning(\"Invalid RTP packet\");\n\t\treturn -1;\n\t}\n\t*start = tmp;\n\treturn (int)(packet->b_wptr - tmp);\n}\n\n/**\n * Obtain the extension header if any.\n * @param packet the RTP packet.\n * @param profile the profile field of the extension header\n * @param start_ext pointer that will be set to the beginning of the payload of the extension header.\n * @return the size of the extension in bytes (the payload size, it can be 0), -1 if parsing of the extension header\n *failed or if no extension is present.\n **/\nint rtp_get_extheader(const mblk_t *packet, uint16_t *profile, uint8_t **start_ext) {\n\tint size = 0;\n\tuint8_t *ext_header;\n\tif (rtp_get_extbit(packet)) {\n\t\text_header = packet->b_rptr + RTP_FIXED_HEADER_SIZE + (rtp_get_cc(packet) * 4);\n\t\tif (ext_header + 4 <= packet->b_wptr) {\n\t\t\tuint32_t h = ntohl(*(uint32_t *)ext_header);\n\t\t\tsize = (int)(h & 0xFFFF);\n\t\t\tif (profile) *profile = (h >> 16);\n\t\t\tsize =\n\t\t\t    (size * 4); /*the size is given in the packet as multiple of 32 bit words, excluding the 4 byte header*/\n\t\t\tif ((ext_header + 4 + size) > packet->b_wptr) {\n\t\t\t\tortp_warning(\"Inconsistent size for rtp extension header\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (start_ext) *start_ext = ext_header + 4;\n\t\t\treturn size;\n\t\t} else {\n\t\t\tortp_warning(\"Insufficient size for rtp extension header.\");\n\t\t\treturn -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/**\n * Delete an extension into the extension header.\n * This function will simply turn the extension into padding.\n * @param packet the RTP packet.\n * @param id the identifier of the extension to delete.\n **/\nvoid rtp_delete_extension_header(mblk_t *packet, int id) {\n\tuint8_t *ext_header, *tmp;\n\tuint16_t profile;\n\tsize_t ext_header_size, size;\n\n\tif (!rtp_get_extbit(packet)) return;\n\n\text_header_size = rtp_get_extheader(packet, &profile, &ext_header);\n\n\tif (ext_header_size == (size_t)-1) {\n\t\treturn;\n\t}\n\n\t// If the profile is set to 0xBEDE then all extensions are represented by a 1-byte header\n\t// If not then by a 2-byte header (cf RFC 8285), not supported by this function\n\ttmp = ext_header;\n\tif (profile != 0xBEDE) {\n\t\treturn;\n\t}\n\twhile (tmp < ext_header + ext_header_size) {\n\t\tif ((int)*tmp == RTP_EXTENSION_MAX) break;\n\n\t\tif ((int)*tmp == RTP_EXTENSION_NONE) {\n\t\t\ttmp += 1; // Padding\n\t\t} else {\n\t\t\tsize = (size_t)(*tmp & 0xF) + 1; // Length is a 4-bit number minus 1\n\t\t\tif (id == (int)(*tmp >> 4)) {    // The id to delete is present\n\t\t\t\t// Just turn it into Padding\n\t\t\t\tmemset(tmp, 0, size + 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttmp += size + 1;\n\t\t}\n\t}\n}\n\nstatic void rtp_add_extension_header_base(mblk_t *packet, int id, size_t size, uint8_t *data, bool_t allocate_buffer) {\n\tif (size <= 0 || data == NULL) {\n\t\tortp_warning(\"Cannot add an extension with empty data\");\n\t\treturn;\n\t}\n\n\tif (!rtp_get_extbit(packet)) {\n\t\tuint16_t *header_p;\n\t\tuint8_t *ext_p;\n\t\tsize_t padding = 0;\n\n\t\tif ((size + 1) % 4 != 0) {\n\t\t\tpadding = 4 - (size + 1) % 4;\n\t\t}\n\n\t\trtp_set_extbit(packet, 0x1);\n\t\tif (allocate_buffer != FALSE) {\n\t\t\t/* total size of ext header will be 4 bytes of global ext header + (1 byte header + size bytes) + padding */\n\t\t\t/* insert a zeroised buffer of this size at the end of the current header and pullup the message */\n\t\t\t/* p_wptr is set at the end of the buffer */\n\t\t\tmsgpullup_with_insert(packet, RTP_FIXED_HEADER_SIZE + (rtp_get_cc(packet) * sizeof(uint32_t)),\n\t\t\t                      size + 5 + padding);\n\t\t}\n\n\t\theader_p = (uint16_t *)(packet->b_rptr + RTP_FIXED_HEADER_SIZE + (rtp_get_cc(packet) * sizeof(uint32_t)));\n\t\t*header_p++ = htons(0xBEDE); // Set the defining profile\n\t\t*header_p++ = htons((uint16_t)((size + 1 + padding) / 4));\n\n\t\text_p = (uint8_t *)header_p;\n\t\t*ext_p++ = (uint8_t)((id << 4) | (size - 1));\n\t\tmemcpy(ext_p, data, size);\n\n\t\tif (padding) {\n\t\t\text_p += size;\n\t\t\tmemset(ext_p, 0, padding);\n\t\t}\n\n\t} else {\n\t\tuint8_t *ext_header, *tmp;\n\t\tuint16_t profile;\n\t\tsize_t ext_header_size, used_size;\n\t\tsize_t used_padding = 0, padding = 0;\n\n\t\text_header_size = rtp_get_extheader(packet, &profile, &ext_header);\n\n\t\tif (profile != 0xBEDE) {\n\t\t\tortp_warning(\"Cannot add extension, profile is not set to 1-byte header\");\n\t\t\treturn;\n\t\t}\n\n\t\t// If the packet already contains this id, then remove it first\n\t\trtp_delete_extension_header(packet, id);\n\n\t\t// Use existing padding if there is since we place it at the end of the extension header\n\t\t// Padding can occur between extensions, it is ignored in this case\n\t\ttmp = ext_header;\n\t\twhile (tmp < ext_header + ext_header_size) {\n\t\t\tif (*tmp == RTP_EXTENSION_NONE) {\n\t\t\t\ttmp += 1; // Padding\n\t\t\t\tused_padding++;\n\t\t\t} else {\n\t\t\t\ttmp += (size_t)(*tmp & 0xF) + 1 + 1; // Length is a 4-bit number minus 1\n\t\t\t\tused_padding = 0;\n\t\t\t}\n\t\t}\n\n\t\tused_size = ext_header_size - used_padding;\n\n\t\tif ((used_size + size + 1) % 4 != 0) {\n\t\t\tpadding = 4 - (used_size + size + 1) % 4;\n\t\t}\n\n\t\t// current ext does not fit in padding left, we must allocate more space\n\t\tif (size + 1 + padding > used_padding) {\n\t\t\tuint16_t *ext_header_size_p;\n\n\t\t\tif (allocate_buffer != FALSE) {\n\t\t\t\t// make room to write the new header ext+padding at the end of current ext header(after final padding)\n\t\t\t\t// b_wptr is moved by msgpullup_with_insert at the end of current message\n\t\t\t\tmsgpullup_with_insert(packet, RTP_FIXED_HEADER_SIZE + (rtp_get_cc(packet) * 4) + 4 + ext_header_size,\n\t\t\t\t                      size + 1 + padding - used_padding);\n\t\t\t\t// msgpullup invalidates packet pointer, so we get them back again\n\t\t\t\text_header_size = rtp_get_extheader(packet, &profile, &ext_header);\n\t\t\t}\n\n\t\t\ttmp = ext_header + used_size;\n\n\t\t\tused_size += size + 1;\n\t\t\text_header_size_p = (uint16_t *)(ext_header - 2);\n\t\t\t*ext_header_size_p = htons((uint16_t)((used_size + padding) / 4));\n\t\t} else {                          // current ext fits in trailing padding, just use it\n\t\t\ttmp = ext_header + used_size; // tmp reached the end including padding, reset it to the end of actual data\n\t\t}\n\n\t\t*tmp++ = (uint8_t)((id << 4) | (size - 1));\n\t\tmemcpy(tmp, data, size);\n\n\t\tif (padding) {\n\t\t\ttmp += size;\n\t\t\tmemset(tmp, 0, padding);\n\t\t}\n\t}\n}\n\n/**\n * Write an extension to the extension header but do not manage memory.\n * Buffer to store the extension header is supposed to be already allocated. Its content will be overwritten\n * b_wptr is not modified\n * @param packet the RTP packet.\n * @param id the identifier of the extension to add.\n * @param size the size in bytes of the extension to add.\n * @param data the buffer to the extension data.\n **/\nvoid rtp_write_extension_header(mblk_t *packet, int id, size_t size, uint8_t *data) {\n\trtp_add_extension_header_base(packet, id, size, data, FALSE);\n}\n\n/**\n * Add an extension to the extension header\n * This function manages the mblk_t memory buffer and extends it if needed\n * This function may reallocate the buffer and pullup the mblk so any reference to b_rptr\n * or b_wptr taken before calling this function may be dangling\n * @param packet the RTP packet.\n * @param id the identifier of the extension to add.\n * @param size the size in bytes of the extension to add.\n * @param data the buffer to the extension data.\n **/\nvoid rtp_add_extension_header(mblk_t *packet, int id, size_t size, uint8_t *data) {\n\trtp_add_extension_header_base(packet, id, size, data, TRUE);\n}\n\n/**\n * Obtain the desired extension in the extension header\n * @param packet the RTP packet.\n * @param id the identifier of the wanted extension\n * @param data pointer that will be set to the beginning of the extension data.\n * @return the size of the wanted extension in bytes, -1 if there is no extension header or the wanted extension was not\n *found.\n **/\nint rtp_get_extension_header(const mblk_t *packet, int id, uint8_t **data) {\n\tuint8_t *ext_header, *tmp;\n\tuint16_t profile;\n\tsize_t ext_header_size, size;\n\n\tif (!rtp_get_extbit(packet)) return -1;\n\n\text_header_size = rtp_get_extheader(packet, &profile, &ext_header);\n\n\tif (ext_header_size == (size_t)-1) {\n\t\treturn -1;\n\t}\n\n\t// If the profile is set to 0xBEDE then all extensions are represented by a 1-byte header\n\t// If not then by a 2-byte header (cf RFC 8285)\n\ttmp = ext_header;\n\tif (profile == 0xBEDE) {\n\t\twhile (tmp < ext_header + ext_header_size) {\n\t\t\tif ((int)*tmp == RTP_EXTENSION_MAX) break;\n\n\t\t\tif ((int)*tmp == RTP_EXTENSION_NONE) {\n\t\t\t\ttmp += 1; // Padding\n\t\t\t} else {\n\t\t\t\tsize = (size_t)(*tmp & 0xF) + 1; // Length is a 4-bit number minus 1\n\n\t\t\t\tif (id == (int)(*tmp >> 4)) {\n\t\t\t\t\tif (data) *data = tmp + 1;\n\t\t\t\t\treturn (int)size;\n\t\t\t\t}\n\n\t\t\t\ttmp += size + 1;\n\t\t\t}\n\t\t}\n\t} else {\n\t\twhile (tmp < ext_header + ext_header_size) {\n\t\t\tif ((int)*tmp == RTP_EXTENSION_NONE) {\n\t\t\t\ttmp += 1;\n\t\t\t} else {\n\t\t\t\tsize = (size_t)(*(tmp + 1));\n\n\t\t\t\tif (id == (int)*tmp) {\n\t\t\t\t\tif (data) *data = tmp + 2;\n\t\t\t\t\treturn (int)size;\n\t\t\t\t}\n\n\t\t\t\ttmp += size + 2;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1;\n}\n\n/**\n * Remaps the IDs of RTP header extensions in the provided packet according to a mapping table.\n *\n * This function examines and updates each extension header in the RTP packet. Extensions with IDs\n * (defined by RTP header extension type) are remapped based on the passed mapping table. The table\n * maps original extension IDs to new ones.\n *\n * @param[in,out] packet Pointer to the RTP packet (mblk_t) where header extensions will be remapped.\n *                       The packet is modified in place.\n * @param[in] mapping    Array containing the mapping for extension IDs. Array indices represent the\n *                       current extension IDs, and their corresponding values define the new IDs.\n *                       Values greater than 0 are valid remappings; IDs with a mapping of 0 are left unchanged.\n */\nvoid rtp_remap_header_extension_ids(mblk_t *packet, const int mapping[16]) {\n\tuint8_t *ext_header, *tmp;\n\tuint16_t profile;\n\tsize_t ext_header_size, size;\n\tint id = 0;\n\n\tif (!rtp_get_extbit(packet)) return;\n\n\text_header_size = rtp_get_extheader(packet, &profile, &ext_header);\n\n\tif (ext_header_size == (size_t)-1) {\n\t\treturn;\n\t}\n\n\ttmp = ext_header;\n\tif (profile == 0xBEDE) { // 1-byte header\n\t\twhile (tmp < ext_header + ext_header_size) {\n\t\t\tif (*tmp == RTP_EXTENSION_MAX) break;\n\n\t\t\tif (*tmp == RTP_EXTENSION_NONE) {\n\t\t\t\ttmp++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tid = *tmp >> 4;\n\t\t\tsize = (size_t)(*tmp & 0xF) + 1; // Length is a 4-bit number minus 1\n\n\t\t\t// Remap the extension id according to the provided array\n\t\t\tif (mapping[id] > 0) *tmp = (uint8_t)((mapping[id] << 4) | (size - 1));\n\n\t\t\ttmp += size + 1;\n\t\t}\n\t} else { // 2-bytes header\n\t\twhile (tmp < ext_header + ext_header_size) {\n\t\t\tif (*tmp == RTP_EXTENSION_NONE) {\n\t\t\t\ttmp++; // Padding\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tid = tmp[0];\n\t\t\tsize = (size_t)tmp[1];\n\n\t\t\t// Remap the extension id according to the provided array\n\t\t\tif (mapping[id] > 0) tmp[0] = (uint8_t)mapping[id];\n\n\t\t\ttmp += size + 2;\n\t\t}\n\t}\n}\n\n/**\n *  Gets last time a valid RTP or RTCP packet was received.\n * @param session RtpSession to get last receive time from.\n * @param tv Pointer to struct timeval to fill.\n *\n **/\nvoid rtp_session_get_last_recv_time(RtpSession *session, struct timeval *tv) {\n#ifdef PERF\n\tortp_error(\"rtp_session_get_last_recv_time() feature disabled.\");\n#else\n\t*tv = session->last_recv_time;\n#endif\n}\n\nuint32_t rtp_session_time_to_ts(RtpSession *session, int millisecs) {\n\tPayloadType *payload;\n\tpayload = rtp_profile_get_payload(session->snd.profile, session->snd.pt);\n\tif (payload == NULL) {\n\t\tortp_warning(\"rtp_session_time_to_ts: use of unsupported payload type %d.\", session->snd.pt);\n\t\treturn 0;\n\t}\n\t/* the return value is in milisecond */\n\treturn (uint32_t)(payload->clock_rate * (double)(millisecs / 1000.0f));\n}\n\n/* function used by the scheduler only:*/\nuint32_t rtp_session_ts_to_time(RtpSession *session, uint32_t timestamp) {\n\tPayloadType *payload;\n\tpayload = rtp_profile_get_payload(session->snd.profile, session->snd.pt);\n\tif (payload == NULL) {\n\t\tortp_warning(\"rtp_session_ts_to_t: use of unsupported payload type %d.\", session->snd.pt);\n\t\treturn 0;\n\t}\n\t/* the return value is in milisecond */\n\treturn (uint32_t)(1000.0 * ((double)timestamp / (double)payload->clock_rate));\n}\n\n/* time is the number of miliseconds elapsed since the start of the scheduler */\nvoid rtp_session_process(RtpSession *session, uint32_t time, RtpScheduler *sched) {\n\twait_point_lock(&session->snd.wp);\n\tif (wait_point_check(&session->snd.wp, time)) {\n\t\tsession_set_set(&sched->w_sessions, session);\n\t\twait_point_wakeup(&session->snd.wp);\n\t}\n\twait_point_unlock(&session->snd.wp);\n\n\twait_point_lock(&session->rcv.wp);\n\tif (wait_point_check(&session->rcv.wp, time)) {\n\t\tsession_set_set(&sched->r_sessions, session);\n\t\twait_point_wakeup(&session->rcv.wp);\n\t}\n\twait_point_unlock(&session->rcv.wp);\n}\n\nvoid rtp_session_set_reuseaddr(RtpSession *session, bool_t yes) {\n\tsession->reuseaddr = yes;\n}\n\ntypedef struct _MetaRtpTransportImpl {\n\tRtpTransport *other_meta_rtp; /*pointer to the \"other\" meta RtpTransport, that is the RTCP transport if we are RTP,\n\t    and the RTP transport if we are RTCP. This is used only for RTCP-mux*/\n\tOList *modifiers;\n\tRtpTransport *endpoint;\n\tbool_t is_rtp;\n\tbool_t has_set_session;\n} MetaRtpTransportImpl;\n\nortp_socket_t meta_rtp_transport_getsocket(RtpTransport *t) {\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)t->data;\n\n\tif (m->endpoint != NULL) {\n\t\treturn m->endpoint->t_getsocket(m->endpoint);\n\t}\n\treturn (m->is_rtp ? t->session->rtp.gs.socket : t->session->rtcp.gs.socket);\n}\n\nvoid meta_rtp_set_session(RtpSession *s, MetaRtpTransportImpl *m) {\n\tOList *elem;\n\t/*if session has not been set yet, do nothing*/\n\tif (s == NULL) {\n\t\treturn;\n\t}\n\n\tif (m->endpoint != NULL) {\n\t\tm->endpoint->session = s;\n\t}\n\tfor (elem = m->modifiers; elem != NULL; elem = o_list_next(elem)) {\n\t\tRtpTransportModifier *rtm = (RtpTransportModifier *)elem->data;\n\t\trtm->session = s;\n\t}\n\tm->has_set_session = TRUE;\n}\n\nstatic int _meta_rtp_transport_send_through_endpoint(\n    RtpTransport *t, mblk_t *msg, int flags, const struct sockaddr *to, socklen_t tolen) {\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)t->data;\n\tint ret;\n\n\tif (m->endpoint != NULL) {\n\t\tret = m->endpoint->t_sendto(m->endpoint, msg, flags, to, tolen);\n\t} else {\n\t\tret = rtp_session_sendto(t->session, m->is_rtp, msg, flags, to, tolen);\n\t}\n\treturn ret;\n}\n\nint meta_rtp_transport_sendto(RtpTransport *t, mblk_t *msg, int flags, const struct sockaddr *to, socklen_t tolen) {\n\tint prev_ret;\n\tint ret = 0;\n\tOList *elem;\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)t->data;\n\n\tif (!m->has_set_session) {\n\t\tmeta_rtp_set_session(t->session, m);\n\t}\n\n\tprev_ret = (int)msgdsize(msg);\n\tfor (elem = m->modifiers; elem != NULL; elem = o_list_next(elem)) {\n\t\tRtpTransportModifier *rtm = (RtpTransportModifier *)elem->data;\n\t\tret = rtm->t_process_on_send(rtm, msg);\n\n\t\tif (ret <= 0) {\n\t\t\t// something went wrong in the modifier (failed to encrypt for instance)\n\t\t\treturn ret;\n\t\t}\n\t\tmsg->b_wptr += (ret - prev_ret);\n\t\tprev_ret = ret;\n\t}\n\tif (!m->is_rtp && t->session->rtcp_mux) { /*if this meta transport is handling RTCP and using rtcp-mux, then we\n\t\t should expedite the packet through the rtp endpoint.*/\n\t\tif (m->other_meta_rtp) {\n\t\t\tret = _meta_rtp_transport_send_through_endpoint(m->other_meta_rtp, msg, flags, to, tolen);\n\t\t} else {\n\t\t\tortp_error(\"meta_rtp_transport_sendto(): rtcp-mux enabled but no RTP meta transport is specified !\");\n\t\t}\n\t} else {\n\t\tret = _meta_rtp_transport_send_through_endpoint(t, msg, flags, to, tolen);\n\t}\n\treturn ret;\n}\n\n/**\n * allow a modifier to inject a packet which will be treated by successive modifiers\n */\nint meta_rtp_transport_modifier_inject_packet_to_send(RtpTransport *t,\n                                                      RtpTransportModifier *tpm,\n                                                      mblk_t *msg,\n                                                      int flags) {\n\tstruct sockaddr *to;\n\tsocklen_t tolen;\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)t->data;\n\n\tif (!m->has_set_session) {\n\t\tmeta_rtp_set_session(t->session, m);\n\t}\n\n\t/* get back socket from transport session */\n\tif (m->is_rtp) {\n\t\tto = (struct sockaddr *)&t->session->rtp.gs.rem_addr;\n\t\ttolen = t->session->rtp.gs.rem_addrlen;\n\t} else {\n\t\tto = (struct sockaddr *)&t->session->rtcp.gs.rem_addr;\n\t\ttolen = t->session->rtcp.gs.rem_addrlen;\n\t}\n\treturn meta_rtp_transport_modifier_inject_packet_to_send_to(t, tpm, msg, flags, to, tolen);\n}\n\n/**\n * allow a modifier to inject a packet which will be treated by successive modifiers\n */\nint meta_rtp_transport_modifier_inject_packet_to_send_to(\n    RtpTransport *t, RtpTransportModifier *tpm, mblk_t *msg, int flags, const struct sockaddr *to, socklen_t tolen) {\n\tint prev_ret;\n\tint ret;\n\tbool_t foundMyself = tpm ? FALSE : TRUE; /*if no modifier, start from the beginning*/\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)t->data;\n\tOList *elem = m->modifiers;\n\n\tif (!m->has_set_session) {\n\t\tmeta_rtp_set_session(t->session, m);\n\t}\n\n\tprev_ret = (int)msgdsize(msg);\n\tfor (; elem != NULL; elem = o_list_next(elem)) {\n\t\t/* run modifiers only after packet injection, the modifier given in parameter is not applied */\n\t\tRtpTransportModifier *rtm = (RtpTransportModifier *)elem->data;\n\t\tif (foundMyself == TRUE) {\n\t\t\tret = rtm->t_process_on_send(rtm, msg);\n\n\t\t\tif (ret <= 0) {\n\t\t\t\t// something went wrong in the modifier (failed to encrypt for instance)\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\tmsg->b_wptr += (ret - prev_ret);\n\t\t\tprev_ret = ret;\n\t\t}\n\n\t\t/* check if we must inject the packet */\n\t\tif (rtm == tpm) {\n\t\t\tfoundMyself = TRUE;\n\t\t}\n\t}\n\n\tret = _meta_rtp_transport_send_through_endpoint(t, msg, flags, to, tolen);\n\tortp_stream_update_sent_bytes(&t->session->rtp.gs, ret);\n\treturn ret;\n}\n\nstatic int _meta_rtp_transport_recv_through_modifiers(RtpTransport *t,\n                                                      RtpTransportModifier *tpm,\n                                                      mblk_t *msg,\n                                                      BCTBX_UNUSED(int flags)) {\n\tint ret = 0;\n\tint prev_ret;\n\tbool_t foundMyself = tpm ? FALSE : TRUE; /*if no modifier, start from the beginning*/\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)t->data;\n\tOList *elem = m->modifiers;\n\tOList *last_elem = NULL;\n\n\tfor (; elem != NULL; elem = o_list_next(elem)) {\n\t\tlast_elem = elem;\n\t}\n\tprev_ret = (int)msgdsize(msg);\n\tret = prev_ret;\n\tfor (; last_elem != NULL; last_elem = o_list_prev(last_elem)) {\n\t\t/* run modifiers only after packet injection, the modifier given in parameter is not applied */\n\t\tRtpTransportModifier *rtm = (RtpTransportModifier *)last_elem->data;\n\n\t\tif (foundMyself == TRUE) {\n\t\t\tret = rtm->t_process_on_receive(rtm, msg);\n\t\t\tif (ret < 0) {\n\t\t\t\t// something went wrong in the modifier (failed to decrypt for instance)\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmsg->b_wptr += (ret - prev_ret);\n\t\t\tprev_ret = ret;\n\t\t}\n\t\t/* check if we must inject the packet */\n\t\tif (rtm == tpm) {\n\t\t\tfoundMyself = TRUE;\n\t\t}\n\t}\n\treturn ret;\n}\n\n/**\n * allow a modifier to inject a packet which will be treated by successive modifiers\n */\nint meta_rtp_transport_modifier_inject_packet_to_recv(RtpTransport *t,\n                                                      RtpTransportModifier *tpm,\n                                                      mblk_t *msg,\n                                                      int flags) {\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)t->data;\n\tint ret = _meta_rtp_transport_recv_through_modifiers(t, tpm, msg, flags);\n\t// passing last_app_ts causes an approximation error in the jitter buffer.\n\trtp_session_process_incoming(t->session, msg, m->is_rtp, msg->reserved1, FALSE);\n\treturn ret;\n}\n\nint meta_rtp_transport_apply_all_except_one_on_receive(RtpTransport *t, RtpTransportModifier *modifier, mblk_t *msg) {\n\tint ret = 0;\n\tint prev_ret;\n\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)t->data;\n\tOList *elem = m->modifiers;\n\tOList *last_elem = NULL;\n\n\tfor (; elem != NULL; elem = o_list_next(elem)) {\n\t\tlast_elem = elem;\n\t}\n\n\tprev_ret = (int)msgdsize(msg);\n\tret = prev_ret;\n\tfor (; last_elem != NULL; last_elem = o_list_prev(last_elem)) {\n\n\t\tRtpTransportModifier *current_modifier = (RtpTransportModifier *)last_elem->data;\n\t\tif (current_modifier == modifier) continue;\n\n\t\tret = current_modifier->t_process_on_receive(current_modifier, msg);\n\t\tif (ret < 0) {\n\t\t\t// something went wrong in the modifier (failed to decrypt for instance)\n\t\t\tbreak;\n\t\t}\n\t\tmsg->b_wptr += (ret - prev_ret);\n\t\tprev_ret = ret;\n\t}\n\treturn ret;\n}\n\nint meta_rtp_transport_recvfrom(RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen) {\n\tint ret;\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)t->data;\n\tOList *elem;\n\tbool_t packet_is_rtcp =\n\t    !m->is_rtp; /* presume it is the same nature as the RtpTransport, but can be changed if rtcp-mux is used */\n\n\tif (!m->has_set_session) {\n\t\tmeta_rtp_set_session(t->session, m);\n\t}\n\n\t/*invoke on schedule on every modifier first, regardless of if a packet is actually received.*/\n\tfor (elem = m->modifiers; elem != NULL; elem = o_list_next(elem)) {\n\t\tRtpTransportModifier *rtm = (RtpTransportModifier *)elem->data;\n\n\t\tif (rtm->t_process_on_schedule) rtm->t_process_on_schedule(rtm);\n\t}\n\n\tif (m->endpoint != NULL) {\n\t\tret = m->endpoint->t_recvfrom(m->endpoint, msg, flags, from, fromlen);\n\t\tif (ret > 0) {\n\t\t\t/*store recv addr for use by modifiers*/\n\t\t\tif (from && fromlen) {\n\t\t\t\tmemcpy(&msg->net_addr, from, *fromlen);\n\t\t\t\tmsg->net_addrlen = *fromlen;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tret = rtp_session_recvfrom(t->session, m->is_rtp, msg, flags, from, fromlen);\n\t}\n\n\tif (ret <= 0) {\n\t\treturn ret;\n\t}\n\tmsg->b_wptr += ret;\n\n\t/*in case of rtcp-mux, we are allowed to reconsider whether it is an RTP or RTCP packet*/\n\tif ((t->session->rtcp_mux || t->session->bundle) && m->is_rtp) {\n\t\tif (ret >= RTP_FIXED_HEADER_SIZE && rtp_get_version(msg) == 2) {\n\t\t\tint pt = rtp_get_payload_type(msg);\n\t\t\tif (pt >= 64 && pt <= 95) {\n\t\t\t\t/*this is assumed to be an RTCP packet*/\n\t\t\t\tpacket_is_rtcp = TRUE;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (m->is_rtp && packet_is_rtcp) {\n\t\tif (m->other_meta_rtp) {\n\t\t\tret = _meta_rtp_transport_recv_through_modifiers(m->other_meta_rtp, NULL, msg, flags);\n\n\t\t} else {\n\t\t\tortp_error(\"RTCP packet received via rtcp-mux but RTCP transport is not set !\");\n\t\t}\n\t} else {\n\t\tret = _meta_rtp_transport_recv_through_modifiers(t, NULL, msg, flags);\n\t}\n\n\t// subtract last written value since it will be rewritten by rtp_session_rtp_recv\n\tmsg->b_wptr -= ret;\n\n\treturn ret;\n}\n\nvoid meta_rtp_transport_close(RtpTransport *t) {\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)t->data;\n\tif (m->endpoint != NULL) {\n\t\tm->endpoint->t_close(m->endpoint);\n\t}\n}\n\nstatic RtpTransport *\n_meta_rtp_transport_new(bool_t is_rtp, RtpTransport *endpoint, unsigned modifiers_count, va_list arguments) {\n\tMetaRtpTransportImpl *m;\n\tRtpTransport *t = ortp_new0(RtpTransport, 1);\n\tm = t->data = ortp_new0(MetaRtpTransportImpl, 1);\n\n\tt->t_getsocket = meta_rtp_transport_getsocket;\n\tt->t_sendto = meta_rtp_transport_sendto;\n\tt->t_recvfrom = meta_rtp_transport_recvfrom;\n\tt->t_close = meta_rtp_transport_close;\n\tt->t_destroy = meta_rtp_transport_destroy;\n\n\tm->is_rtp = is_rtp;\n\tm->endpoint = endpoint;\n\twhile (modifiers_count != 0) {\n\t\tm->modifiers = o_list_append(m->modifiers, va_arg(arguments, RtpTransportModifier *));\n\t\tmodifiers_count--;\n\t}\n\n\treturn t;\n}\n\nRtpTransport *meta_rtp_transport_new(RtpTransport *endpoint, unsigned modifiers_count, ...) {\n\tRtpTransport *tr;\n\tva_list args;\n\tva_start(args, modifiers_count);\n\ttr = _meta_rtp_transport_new(TRUE, endpoint, modifiers_count, args);\n\tva_end(args);\n\treturn tr;\n}\n\nRtpTransport *meta_rtcp_transport_new(RtpTransport *endpoint, unsigned modifiers_count, ...) {\n\tRtpTransport *tr;\n\tva_list args;\n\tva_start(args, modifiers_count);\n\ttr = _meta_rtp_transport_new(FALSE, endpoint, modifiers_count, args);\n\tva_end(args);\n\treturn tr;\n}\n\n/*this links both meta rtp transport, which is necessary for rtcp-mux to work*/\nvoid meta_rtp_transport_link(RtpTransport *rtp, RtpTransport *rtcp) {\n\tMetaRtpTransportImpl *mrtp = (MetaRtpTransportImpl *)rtp->data;\n\tMetaRtpTransportImpl *mrtcp = (MetaRtpTransportImpl *)rtcp->data;\n\tmrtp->other_meta_rtp = rtcp;\n\tmrtcp->other_meta_rtp = rtp;\n}\n\nRtpTransport *meta_rtp_transport_get_endpoint(const RtpTransport *transport) {\n\treturn transport->data ? ((MetaRtpTransportImpl *)transport->data)->endpoint : NULL;\n}\n\nvoid meta_rtp_transport_set_endpoint(RtpTransport *transport, RtpTransport *endpoint) {\n\tif (transport->data) {\n\t\t((MetaRtpTransportImpl *)transport->data)->endpoint = endpoint;\n\t} else ortp_error(\"Cannot set endpoint [%p] on rtp transport [%p]\", transport, endpoint);\n}\n\nvoid meta_rtp_transport_destroy(RtpTransport *tp) {\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)tp->data;\n\tOList *elem;\n\n\tif (m->endpoint != NULL) {\n\t\tm->endpoint->t_destroy(m->endpoint);\n\t}\n\n\tfor (elem = m->modifiers; elem != NULL; elem = o_list_next(elem)) {\n\t\tRtpTransportModifier *rtm = (RtpTransportModifier *)elem->data;\n\t\trtm->transport = NULL;\n\t\trtm->t_destroy(rtm);\n\t}\n\to_list_free(m->modifiers);\n\n\tortp_free(m);\n\tortp_free(tp);\n}\n\nint rtp_transport_modifier_level_compare(const RtpTransportModifier *modifier_a,\n                                         const RtpTransportModifier *modifier_b) {\n\treturn modifier_a->level - modifier_b->level;\n}\n\nvoid meta_rtp_transport_remove_modifier(RtpTransport *tp, RtpTransportModifier *tpm) {\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)tp->data;\n\tm->modifiers = o_list_remove(m->modifiers, tpm);\n}\n\nvoid meta_rtp_transport_append_modifier(RtpTransport *tp, RtpTransportModifier *tpm) {\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)tp->data;\n\ttpm->transport = tp;\n\tm->modifiers = o_list_insert_sorted(m->modifiers, tpm, (bctbx_compare_func)rtp_transport_modifier_level_compare);\n\tif (m->has_set_session) {\n\t\ttpm->session = tp->session;\n\t}\n}\n\nvoid meta_rtp_transport_prepend_modifier(RtpTransport *tp, RtpTransportModifier *tpm) {\n\tMetaRtpTransportImpl *m = (MetaRtpTransportImpl *)tp->data;\n\ttpm->transport = tp;\n\tm->modifiers = o_list_insert_sorted(m->modifiers, tpm, (bctbx_compare_func)rtp_transport_modifier_level_compare);\n\tif (m->has_set_session) {\n\t\ttpm->session = tp->session;\n\t}\n}\n\nbool_t rtp_session_get_symmetric_rtp(const RtpSession *session) {\n\treturn session->symmetric_rtp;\n}\n\nint rtp_session_splice(RtpSession *session, RtpSession *to_session) {\n\tif (session->spliced_session) {\n\t\tortp_error(\"rtp_session_splice(): session %p already splicing into session %p\", session,\n\t\t           session->spliced_session);\n\t\treturn -1;\n\t}\n\tsession->spliced_session = to_session;\n\tto_session->is_spliced = TRUE;\n\tortp_message(\"rtp_session_splice(): session %p splicing to %p\", session, to_session);\n\treturn 0;\n}\n\nint rtp_session_unsplice(RtpSession *session, RtpSession *to_session) {\n\tif (session->spliced_session != to_session) {\n\t\tortp_error(\"rtp_session_unsplice() session %p is not spliced to session %p\", session, to_session);\n\t\treturn -1;\n\t}\n\tsession->spliced_session = NULL;\n\tto_session->is_spliced = FALSE;\n\tortp_message(\"rtp_session_unsplice(): session %p no longer splicing to %p\", session, to_session);\n\treturn 0;\n}\n\n/*\n * send packet through the peered session, the mblk_t is not freed.\n **/\nvoid rtp_session_do_splice(RtpSession *session, mblk_t *packet, bool_t is_rtp) {\n\tRtpSession *peer = session->spliced_session;\n\tif (peer) {\n\t\tOrtpStream *os = is_rtp ? &peer->rtp.gs : &peer->rtcp.gs;\n\t\t_ortp_sendto(os->socket, packet, 0, (struct sockaddr *)&os->rem_addr, os->rem_addrlen);\n\t}\n}\n\nbool_t ortp_stream_is_ipv6(OrtpStream *os) {\n\tif (os->sockfamily == AF_INET6) {\n\t\tstruct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&os->rem_addr;\n\t\treturn !IN6_IS_ADDR_V4MAPPED(&in6->sin6_addr);\n\t}\n\treturn FALSE;\n}\n\nvoid rtp_session_reset_recvfrom(RtpSession *session) {\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\tortp_mutex_lock(&session->rtp.winthread_lock);\n\tif (session->rtp.is_win_thread_running) {\n\t\tsession->rtp.is_win_thread_running = FALSE;\n\t\tortp_thread_join(session->rtp.win_t, NULL);\n\t}\n\tortp_mutex_unlock(&session->rtp.winthread_lock);\n#endif\n\tflushq(&session->rtp.winrq, FLUSHALL);\n}\n\nvoid rtp_session_enable_transfer_mode(RtpSession *session, bool_t enable) {\n\tsession->transfer_mode = enable;\n\n\t// Disable other features when this one is enabled\n\tif (enable) {\n\t\trtp_session_enable_jitter_buffer(session, FALSE);\n\t\tif (session->fec_stream != NULL) {\n\t\t\tRtpSession *fec_session = fec_stream_get_fec_session(session->fec_stream);\n\t\t\tif (fec_session) {\n\t\t\t\trtp_session_destroy(fec_session);\n\t\t\t\tfec_session = NULL;\n\t\t\t}\n\t\t\tfec_stream_destroy(session->fec_stream);\n\t\t\tsession->fec_stream = NULL;\n\t\t}\n\t}\n}\n\nbool_t rtp_session_transfer_mode_enabled(RtpSession *session) {\n\treturn session->transfer_mode;\n}\n\nconst abe_stats_t *rtp_session_get_audio_bandwidth_estimator_stats(RtpSession *session) {\n\tif (session->rtp.audio_bw_estimator) {\n\t\treturn &session->rtp.audio_bw_estimator->stats;\n\t} else {\n\t\treturn NULL;\n\t}\n}\n\nint rtp_session_get_audio_bandwidth_estimator_duplicate_rate(RtpSession *session) {\n\tif (session->rtp.audio_bw_estimator) {\n\t\treturn (int)ortp_audio_bandwidth_estimator_get_duplicate_rate(session->rtp.audio_bw_estimator);\n\t} else {\n\t\treturn -1;\n\t}\n}\n\nvoid rtp_session_set_bundle(RtpSession *session, RtpBundle *bundle) {\n\tortp_mutex_lock(&session->main_mutex);\n\tsession->bundle = bundle;\n\tortp_mutex_unlock(&session->main_mutex);\n}\n"
  },
  {
    "path": "src/rtpsession_inet.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE\n#endif\n\n#if __APPLE__\n#include \"TargetConditionals.h\"\n#endif\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\" /*needed for HAVE_SYS_UIO_H and HAVE_ARC4RANDOM */\n#endif\n#include \"ortp/ortp.h\"\n#include \"ortp/str_utils.h\"\n#include \"rtpsession_priv.h\"\n#include \"utils.h\"\n#include <bctoolbox/defs.h>\n#include <bctoolbox/port.h>\n\n#if (_WIN32_WINNT >= 0x0600)\n#include <delayimp.h>\n#undef ExternC\n#ifdef ORTP_WINDOWS_DESKTOP\n#include <QOS2.h>\n#include <VersionHelpers.h>\n#endif\n#endif\n\n#if (defined(_WIN32) || defined(_WIN32_WCE)) && defined(ORTP_WINDOWS_DESKTOP)\n#include <mstcpip.h>\n#include <mswsock.h>\n// On Windows, SO_TIMESTAMP is only defined if NTDDI_VERSION >= NTDDI_WIN10_FE. ie: it is only available for Windows10\n// 20h2 (10/08/2020).\n// => CMakeLists.txt may add add_definitions(-DNTDDI_VERSION=NTDDI_WIN10_FE -D_WIN32_WINNT=0x0A00) to limit minimum\n// version. From this version, we can read the timestamp through the SIO_TIMESTAMPING IOCTL.\n// https://docs.microsoft.com/en-us/windows/win32/winsock/winsock-timestamping This is not currently implemented because\n// of unworking WSAIoctl and undocumented failures (usually WSAEOPNOTSUPP=10045). Check of \"WSAIoctl configured\n// successfully timestamping\" in logs to look forward on success cases.\n#include <iphlpapi.h>\n#endif\n\n// Define available TIMESTAMPING values and use it for receive message controls.\n#ifdef SCM_TIMESTAMP\n#define _RECV_SO_TIMESTAMP_TYPE SCM_TIMESTAMP\n#elif defined(SO_TIMESTAMP)\n#define _RECV_SO_TIMESTAMP_TYPE SO_TIMESTAMP\n#else\n#undef _RECV_SO_TIMESTAMP_TYPE // Just in case\n#endif\n\n#ifdef HAVE_SYS_UIO_H\n#include <sys/uio.h>\n#ifdef HAVE_RECVMSG\n#define USE_RECVMSG 1\n#endif\n#ifdef HAVE_SENDMSG\n#define USE_SENDMSG 1\n#endif\n#endif\n\n#define can_connect(s) ((s)->use_connect && !(s)->symmetric_rtp)\n\n#if defined(_WIN32) || defined(_WIN32_WCE)\n#ifndef WSAID_WSARECVMSG\n/* http://source.winehq.org/git/wine.git/blob/HEAD:/include/mswsock.h */\n#define WSAID_WSARECVMSG {0xf689d7c8, 0x6f1f, 0x436b, {0x8a, 0x53, 0xe5, 0x4f, 0xe3, 0x51, 0xc3, 0x22}}\n#ifndef MAX_NATURAL_ALIGNMENT\n#define MAX_NATURAL_ALIGNMENT sizeof(DWORD)\n#endif\n#ifndef TYPE_ALIGNMENT\n#define TYPE_ALIGNMENT(t)                                                                                              \\\n\tFIELD_OFFSET(                                                                                                      \\\n\t    struct {                                                                                                       \\\n\t\t    char x;                                                                                                    \\\n\t\t    t test;                                                                                                    \\\n\t    },                                                                                                             \\\n\t    test)\n#endif\ntypedef WSACMSGHDR *LPWSACMSGHDR;\n#ifndef WSA_CMSGHDR_ALIGN\n#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR) - 1) & (~(TYPE_ALIGNMENT(WSACMSGHDR) - 1)))\n#endif\n#ifndef WSA_CMSGDATA_ALIGN\n#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT - 1) & (~(MAX_NATURAL_ALIGNMENT - 1)))\n#endif\n#ifndef WSA_CMSG_FIRSTHDR\n#define WSA_CMSG_FIRSTHDR(msg)                                                                                         \\\n\t(((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL)\n#endif\n#ifndef WSA_CMSG_NXTHDR\n#define WSA_CMSG_NXTHDR(msg, cmsg)                                                                                     \\\n\t((!(cmsg)) ? WSA_CMSG_FIRSTHDR(msg)                                                                                \\\n\t           : ((((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len) + sizeof(WSACMSGHDR)) >                     \\\n\t               (u_char *)((msg)->Control.buf) + (msg)->Control.len)                                                \\\n\t                  ? (LPWSACMSGHDR)NULL                                                                             \\\n\t                  : (LPWSACMSGHDR)((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len))))\n#endif\n#ifndef WSA_CMSG_DATA\n#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)))\n#endif\n#endif\n#undef CMSG_FIRSTHDR\n#define CMSG_FIRSTHDR WSA_CMSG_FIRSTHDR\n#undef CMSG_NXTHDR\n#define CMSG_NXTHDR WSA_CMSG_NXTHDR\n#undef CMSG_DATA\n#define CMSG_DATA WSA_CMSG_DATA\ntypedef INT(WINAPI *LPFN_WSARECVMSG)(SOCKET, LPWSAMSG, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE);\nstatic LPFN_WSARECVMSG ortp_WSARecvMsg = NULL;\n\n#endif\n\n#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__QNX__)\n/* Mingw32 does not define AI_V4MAPPED, however it is supported starting from Windows Vista. QNX also does not define\n * AI_V4MAPPED. */\n#ifndef AI_V4MAPPED\n#define AI_V4MAPPED 0x00000800\n#endif\n#ifndef AI_ALL\n#define AI_ALL 0x00000100\n#endif\n#ifndef IPV6_V6ONLY\n#define IPV6_V6ONLY 27\n#endif\n#endif\n\n#ifndef IN6_IS_ADDR_MULTICAST\n#define IN6_IS_ADDR_MULTICAST(i) (((uint8_t *)(i))[0] == 0xff)\n#endif\n\nstatic int _rtp_session_set_remote_addr_full(\n    RtpSession *session, const char *rtp_addr, int rtp_port, const char *rtcp_addr, int rtcp_port, bool_t is_aux);\n\nstatic bool_t try_connect(ortp_socket_t fd, const struct sockaddr *dest, socklen_t addrlen) {\n\tif (connect(fd, dest, addrlen) < 0) {\n\t\tortp_warning(\"Could not connect() socket: %s\", getSocketError());\n\t\treturn FALSE;\n\t}\n\treturn TRUE;\n}\n\nstatic int set_multicast_group(ortp_socket_t sock, const char *addr) {\n#ifndef __hpux\n\tstruct addrinfo *res;\n\tint err;\n\n\tres = bctbx_name_to_addrinfo(AF_UNSPEC, SOCK_DGRAM, addr, 0);\n\tif (res == NULL) return -1;\n\n\tswitch (res->ai_family) {\n\t\tcase AF_INET:\n\t\t\tif (IN_MULTICAST(ntohl(((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr))) {\n\t\t\t\tstruct ip_mreq mreq;\n\t\t\t\tmreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr;\n\t\t\t\tmreq.imr_interface.s_addr = INADDR_ANY;\n\t\t\t\terr = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (SOCKET_OPTION_VALUE)&mreq, sizeof(mreq));\n\t\t\t\tif (err < 0) {\n\t\t\t\t\tortp_warning(\"Fail to join address group: %s.\", getSocketError());\n\t\t\t\t} else {\n\t\t\t\t\tortp_message(\"RTP socket [%i] has joined address group [%s]\", sock, addr);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase AF_INET6:\n\t\t\tif IN6_IS_ADDR_MULTICAST (&(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)) {\n\t\t\t\tstruct ipv6_mreq mreq;\n\t\t\t\tmreq.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;\n\t\t\t\tmreq.ipv6mr_interface = 0;\n\t\t\t\terr = setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (SOCKET_OPTION_VALUE)&mreq, sizeof(mreq));\n\t\t\t\tif (err < 0) {\n\t\t\t\t\tortp_warning(\"Fail to join address group: %s.\", getSocketError());\n\t\t\t\t} else {\n\t\t\t\t\tortp_message(\"RTP socket 6 [%i] has joined address group [%s]\", sock, addr);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t}\n\tfreeaddrinfo(res);\n\treturn 0;\n#else\n\treturn -1;\n#endif\n}\n\nstatic ortp_socket_t create_and_bind(const char *addr,\n                                     int *port,\n                                     int *sock_family,\n                                     bool_t reuse_addr,\n                                     struct sockaddr_storage *bound_addr,\n                                     socklen_t *bound_addr_len) {\n\tint err;\n\tint optval = 1;\n\tortp_socket_t sock = -1;\n\tstruct addrinfo *res0, *res;\n\n\tif (*port == -1) *port = 0;\n\tif (*port == 0) reuse_addr = FALSE;\n\n\tres0 = bctbx_name_to_addrinfo(AF_UNSPEC, SOCK_DGRAM, addr, *port);\n\tif (res0 == NULL) {\n\t\tortp_error(\"Cannot create addrinfo from address [%s], port [%i].\", addr, *port);\n\t\treturn -1;\n\t}\n\n\tfor (res = res0; res; res = res->ai_next) {\n\t\tbool_t isMulticast = bctbx_is_multicast_addr(res->ai_addr);\n\t\tstruct addrinfo *any = NULL;\n\n\t\tsock = socket(res->ai_family, res->ai_socktype, 0);\n\t\tif (sock == -1) {\n\t\t\tortp_error(\"Cannot create a socket with family=[%i] and socktype=[%i]: %s\", res->ai_family,\n\t\t\t           res->ai_socktype, getSocketError());\n\t\t\tcontinue;\n\t\t}\n\t\tif (reuse_addr) {\n\t\t\terr = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (SOCKET_OPTION_VALUE)&optval, sizeof(optval));\n\t\t\tif (err < 0) {\n\t\t\t\tortp_warning(\"Fail to set rtp address reusable: %s.\", getSocketError());\n\t\t\t}\n#ifdef SO_REUSEPORT\n\t\t\t/*SO_REUSEPORT is required on mac and ios especially for doing multicast*/\n\t\t\terr = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (SOCKET_OPTION_VALUE)&optval, sizeof(optval));\n\t\t\tif (err < 0) {\n\t\t\t\tortp_warning(\"Fail to set rtp port reusable: %s.\", getSocketError());\n\t\t\t}\n#endif\n\t\t}\n\t\t/*enable dual stack operation, default is enabled on unix, disabled on windows.*/\n\t\tif (res->ai_family == AF_INET6) {\n\t\t\toptval = 0;\n\t\t\terr = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&optval, sizeof(optval));\n\t\t\tif (err < 0) {\n\t\t\t\tortp_warning(\"Fail to IPV6_V6ONLY: %s.\", getSocketError());\n\t\t\t}\n\t\t}\n\n#ifdef SO_TIMESTAMP\n\t\toptval = 1;\n\t\terr = setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, (SOCKET_OPTION_VALUE)&optval, sizeof(optval));\n\t\tif (err < 0) {\n\t\t\tortp_warning(\"Fail to set rtp timestamp: %s.\", getSocketError());\n\t\t}\n#ifdef _WIN32\n\t\tDWORD bytes_returned;\n\t\t// Configure tx timestamp reception.\n\t\tTIMESTAMPING_CONFIG config = {0};\n\t\tconfig.Flags |= TIMESTAMPING_FLAG_RX;\n\t\tif (WSAIoctl(sock, SIO_TIMESTAMPING, &config, sizeof(config), NULL, 0, &bytes_returned, NULL, NULL) ==\n\t\t    SOCKET_ERROR) {\n\t\t\tortp_warning(\"WSAIoctl cannot configure timestamping %d\\n\", WSAGetLastError());\n\t\t} else {\n\t\t\tortp_message(\"WSAIoctl configured successfully timestamping with SIO_TIMESTAMPING\\n\");\n\t\t}\n#endif\n#endif\n\t\terr = 0;\n\t\toptval = 1;\n\t\tswitch (res->ai_family) {\n\t\t\tdefault:\n\t\t\tcase AF_INET:\n#ifdef IP_RECVTTL\n\t\t\t\terr = setsockopt(sock, IPPROTO_IP, IP_RECVTTL, (SOCKET_OPTION_VALUE)&optval, sizeof(optval));\n#endif\n\t\t\t\tbreak;\n\t\t\tcase AF_INET6:\n#ifdef IPV6_RECVHOPLIMIT\n\t\t\t\terr = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, (SOCKET_OPTION_VALUE)&optval, sizeof(optval));\n#endif\n\t\t\t\tbreak;\n\t\t}\n\t\tif (err < 0) {\n\t\t\tortp_warning(\"Fail to set recv TTL/HL socket option: %s.\", getSocketError());\n\t\t}\n\t\tif (isMulticast) {\n\t\t\t/* Multicast membership must be claimed before bind(), otherwise we take the risk of\n\t\t\t * getting our bind() rejected because the local unicast port is already used.\n\t\t\t * In this case we should not bind() to the multicast address itself, but to a local address.\n\t\t\t * Use ::0 or 0.0.0.0.\n\t\t\t */\n\t\t\tset_multicast_group(sock, addr);\n\t\t\tany = bctbx_name_to_addrinfo(res->ai_family, SOCK_DGRAM, res->ai_family == AF_INET6 ? \"::0\" : \"0.0.0.0\",\n\t\t\t                             *port);\n\t\t}\n\t\t*sock_family = res->ai_family;\n\t\terr = bind(sock, any ? any->ai_addr : res->ai_addr, any ? (int)any->ai_addrlen : (int)res->ai_addrlen);\n\t\tif (any) bctbx_freeaddrinfo(any);\n\t\tif (err != 0) {\n\t\t\tortp_error(\"Fail to bind rtp/rtcp socket to (addr=%s port=%i) : %s.\", addr, *port, getSocketError());\n\t\t\tclose_socket(sock);\n\t\t\tsock = -1;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\n\tbctbx_freeaddrinfo(res0);\n\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\tif (ortp_WSARecvMsg == NULL) {\n\t\tGUID guid = WSAID_WSARECVMSG;\n\t\tDWORD bytes_returned;\n\t\tif (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &ortp_WSARecvMsg,\n\t\t             sizeof(ortp_WSARecvMsg), &bytes_returned, NULL, NULL) == SOCKET_ERROR) {\n\t\t\tortp_warning(\"WSARecvMsg function not found [%d].\", WSAGetLastError());\n\t\t}\n\t}\n#endif\n\tif (sock != -1) {\n\t\tstruct sockaddr_storage saddr;\n\t\tsocklen_t slen = sizeof(saddr);\n\n\t\tset_non_blocking_socket(sock);\n\t\terr = getsockname(sock, (struct sockaddr *)&saddr, &slen);\n\t\tif (err == -1) {\n\t\t\tortp_error(\"getsockname(): %s\", getSocketError());\n\t\t\tclose_socket(sock);\n\t\t\treturn (ortp_socket_t)-1;\n\t\t}\n\t\t/*update the bind address, especially useful if requested port was 0 (random)*/\n\t\tmemcpy(bound_addr, &saddr, slen);\n\t\t*bound_addr_len = slen;\n\t\tif (*port == 0) {\n\t\t\terr = bctbx_sockaddr_to_ip_address((struct sockaddr *)&saddr, slen, NULL, 0, port);\n\t\t\tif (err != 0) {\n\t\t\t\tclose_socket(sock);\n\t\t\t\treturn (ortp_socket_t)-1;\n\t\t\t}\n\t\t}\n\t}\n\treturn sock;\n}\n\nvoid _rtp_session_apply_socket_sizes(RtpSession *session) {\n\tint err;\n\tbool_t done = FALSE;\n\tortp_socket_t sock = session->rtp.gs.socket;\n\tunsigned int sndbufsz = session->rtp.snd_socket_size;\n\tunsigned int rcvbufsz = session->rtp.rcv_socket_size;\n\n\tif (sock == (ortp_socket_t)-1) return;\n\n\tif (sndbufsz > 0) {\n#ifdef SO_SNDBUFFORCE\n\t\terr = setsockopt(sock, SOL_SOCKET, SO_SNDBUFFORCE, (void *)&sndbufsz, sizeof(sndbufsz));\n\t\tif (err == -1) {\n\t\t\tortp_warning(\"Fail to increase socket's send buffer size with SO_SNDBUFFORCE: %s.\", getSocketError());\n\t\t} else done = TRUE;\n#endif\n\t\tif (!done) {\n\t\t\terr = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&sndbufsz, sizeof(sndbufsz));\n\t\t\tif (err == -1) {\n\t\t\t\tortp_error(\"Fail to increase socket's send buffer size with SO_SNDBUF: %s.\", getSocketError());\n\t\t\t}\n\t\t}\n\t}\n\tdone = FALSE;\n\tif (rcvbufsz > 0) {\n#ifdef SO_RCVBUFFORCE\n\t\terr = setsockopt(sock, SOL_SOCKET, SO_RCVBUFFORCE, (void *)&rcvbufsz, sizeof(rcvbufsz));\n\t\tif (err == -1) {\n\t\t\tortp_warning(\"Fail to increase socket's recv buffer size with SO_RCVBUFFORCE: %s.\", getSocketError());\n\t\t}\n#endif\n\t\tif (!done) {\n\t\t\terr = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbufsz, sizeof(rcvbufsz));\n\t\t\tif (err == -1) {\n\t\t\t\tortp_error(\"Fail to increase socket's recv buffer size with SO_RCVBUF: %s.\", getSocketError());\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n *rtp_session_set_local_addr:\n *@param session:\t\ta rtp session freshly created.\n *@param addr:\t\ta local IP address in the xxx.xxx.xxx.xxx form.\n *@param rtp_port:\t\ta local port or -1 to let oRTP choose the port randomly\n *@param rtcp_port:\t\ta local port or -1 to let oRTP choose the port randomly\n *\n *\tSpecify the local addr to be use to listen for rtp packets or to send rtp packet from.\n *\tIn case where the rtp session is send-only, then it is not required to call this function:\n *\twhen calling rtp_session_set_remote_addr(), if no local address has been set, then the\n *\tdefault INADRR_ANY (0.0.0.0) IP address with a random port will be used. Calling\n *\trtp_session_set_local_addr() is mandatory when the session is recv-only or duplex.\n *\n *\tReturns: 0 on success.\n **/\n\nint rtp_session_set_local_addr(RtpSession *session, const char *addr, int rtp_port, int rtcp_port) {\n\tortp_socket_t sock;\n\tint sockfamily;\n\n\t// Stop async rtp recv thread before recreating the socket\n\trtp_session_reset_recvfrom(session);\n\n\tif (session->rtp.gs.socket != (ortp_socket_t)-1) {\n\t\t/* don't rebind, but close before*/\n\t\t_rtp_session_release_sockets(session, FALSE);\n\t}\n\t/* try to bind the rtp port */\n\n\tsock = create_and_bind(addr, &rtp_port, &sockfamily, session->reuseaddr, &session->rtp.gs.loc_addr,\n\t                       &session->rtp.gs.loc_addrlen);\n\tif (sock != -1) {\n\t\tsession->rtp.gs.sockfamily = sockfamily;\n\t\tsession->rtp.gs.socket = sock;\n\t\tsession->rtp.gs.loc_port = rtp_port;\n\t\t_rtp_session_apply_socket_sizes(session);\n\t\t/*try to bind rtcp port */\n\t\tsock = create_and_bind(addr, &rtcp_port, &sockfamily, session->reuseaddr, &session->rtcp.gs.loc_addr,\n\t\t                       &session->rtcp.gs.loc_addrlen);\n\t\tif (sock != (ortp_socket_t)-1) {\n\t\t\tsession->rtcp.gs.sockfamily = sockfamily;\n\t\t\tsession->rtcp.gs.socket = sock;\n\t\t\tsession->rtcp.gs.loc_port = rtcp_port;\n\t\t} else {\n\t\t\tortp_error(\"Could not create and bind rtcp socket for session [%p]\", session);\n\t\t\treturn -1;\n\t\t}\n\n\t\t/* set socket options (but don't change chosen states) */\n\t\trtp_session_set_multicast_ttl(session, -1);\n\t\trtp_session_set_multicast_loopback(session, -1);\n\t\tif (session->use_pktinfo) rtp_session_set_pktinfo(session, TRUE);\n\t\tortp_message(\"RtpSession bound to [%s] ports [%i] [%i]\", addr, rtp_port, rtcp_port);\n\t\treturn 0;\n\t}\n\tortp_error(\"Could not bind RTP socket to %s on port %i for session [%p]\", addr, rtp_port, session);\n\treturn -1;\n}\n\nstatic void _rtp_session_recreate_sockets(RtpSession *session) {\n\tchar addr[NI_MAXHOST];\n\tint err = bctbx_sockaddr_to_ip_address((struct sockaddr *)&session->rtp.gs.loc_addr, session->rtp.gs.loc_addrlen,\n\t                                       addr, sizeof(addr), NULL);\n\tif (err != 0) return;\n\t/*re create and bind sockets as they were done previously*/\n\tortp_message(\"RtpSession %p is going to re-create its socket.\", session);\n\trtp_session_set_local_addr(session, addr, session->rtp.gs.loc_port, session->rtcp.gs.loc_port);\n}\n\nstatic void _rtp_session_check_socket_refresh(RtpSession *session) {\n\tif (session->flags & RTP_SESSION_SOCKET_REFRESH_REQUESTED) {\n\t\tsession->flags &= ~RTP_SESSION_SOCKET_REFRESH_REQUESTED;\n\t\t_rtp_session_recreate_sockets(session);\n\t}\n}\n\n/**\n * Requests the session to re-create and bind its RTP and RTCP sockets same as they are currently.\n * This is used when a change in the routing rules of the host or process was made, in order to have\n * this routing rules change taking effect on the RTP/RTCP packets sent by the session.\n **/\nvoid rtp_session_refresh_sockets(RtpSession *session) {\n\tif (session->rtp.gs.socket != (ortp_socket_t)-1) {\n\t\tsession->flags |= RTP_SESSION_SOCKET_REFRESH_REQUESTED;\n\t}\n}\n\nint rtp_session_join_multicast_group(RtpSession *session, const char *ip) {\n\tint err;\n\tif (session->rtp.gs.socket == (ortp_socket_t)-1) {\n\t\tortp_error(\"rtp_session_set_multicast_group() must be done only on bound sockets, use \"\n\t\t           \"rtp_session_set_local_addr() first\");\n\t\treturn -1;\n\t}\n\terr = set_multicast_group(session->rtp.gs.socket, ip);\n\tset_multicast_group(session->rtcp.gs.socket, ip);\n\treturn err;\n}\n\n/**\n *rtp_session_set_pktinfo:\n *@param session: a rtp session\n *@param activate: activation flag (0 to deactivate, other value to activate)\n *\n * (De)activates packet info for incoming and outgoing packets.\n *\n * Returns: 0 on success.\n *\n **/\nint rtp_session_set_pktinfo(RtpSession *session, int activate) {\n\tint retval;\n\tint optname;\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\tchar optval[sizeof(DWORD)];\n\tint optlen = sizeof(optval);\n#else\n\tint *optval = &activate;\n\tint optlen = sizeof(activate);\n#endif\n\tsession->use_pktinfo = activate;\n\t// Dont't do anything if socket hasn't been created yet\n\tif (session->rtp.gs.socket == (ortp_socket_t)-1) return 0;\n\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\tmemset(optval, activate, sizeof(optval));\n#endif\n\n#ifdef IP_PKTINFO\n\toptname = IP_PKTINFO;\n#else\n\toptname = IP_RECVDSTADDR;\n#endif\n\tretval = setsockopt(session->rtp.gs.socket, IPPROTO_IP, optname, optval, optlen);\n\tif (retval < 0) {\n\t\tortp_warning(\"Fail to set IPv4 packet info on RTP socket: %s.\", getSocketError());\n\t}\n\tretval = setsockopt(session->rtcp.gs.socket, IPPROTO_IP, optname, optval, optlen);\n\tif (retval < 0) {\n\t\tortp_warning(\"Fail to set IPv4 packet info on RTCP socket: %s.\", getSocketError());\n\t}\n\n\tif (session->rtp.gs.sockfamily != AF_INET) {\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\t\tmemset(optval, activate, sizeof(optval));\n#endif\n\n#ifdef IPV6_RECVPKTINFO\n\t\toptname = IPV6_RECVPKTINFO;\n#else\n\t\toptname = IPV6_RECVDSTADDR;\n#endif\n\t\tretval = setsockopt(session->rtp.gs.socket, IPPROTO_IPV6, optname, optval, optlen);\n\t\tif (retval < 0) {\n\t\t\tortp_warning(\"Fail to set IPv6 packet info on RTP socket: %s.\", getSocketError());\n\t\t}\n\t\tretval = setsockopt(session->rtcp.gs.socket, IPPROTO_IPV6, optname, optval, optlen);\n\t\tif (retval < 0) {\n\t\t\tortp_warning(\"Fail to set IPv6 packet info on RTCP socket: %s.\", getSocketError());\n\t\t}\n\t}\n\n\treturn retval;\n}\n\n/**\n *rtp_session_set_multicast_ttl:\n *@param session: a rtp session\n *@param ttl: desired Multicast Time-To-Live\n *\n * Sets the TTL (Time-To-Live) for outgoing multicast packets.\n *\n * Returns: 0 on success.\n *\n **/\nint rtp_session_set_multicast_ttl(RtpSession *session, int ttl) {\n\tint retval;\n\n\t// Store new TTL if one is specified\n\tif (ttl > 0) session->multicast_ttl = ttl;\n\n\t// Don't do anything if socket hasn't been created yet\n\tif (session->rtp.gs.socket == (ortp_socket_t)-1) return 0;\n\n\tswitch (session->rtp.gs.sockfamily) {\n\t\tcase AF_INET: {\n\n\t\t\tretval = setsockopt(session->rtp.gs.socket, IPPROTO_IP, IP_MULTICAST_TTL,\n\t\t\t                    (SOCKET_OPTION_VALUE)&session->multicast_ttl, sizeof(session->multicast_ttl));\n\n\t\t\tif (retval < 0) break;\n\n\t\t\tretval = setsockopt(session->rtcp.gs.socket, IPPROTO_IP, IP_MULTICAST_TTL,\n\t\t\t                    (SOCKET_OPTION_VALUE)&session->multicast_ttl, sizeof(session->multicast_ttl));\n\n\t\t} break;\n\t\tcase AF_INET6: {\n\n\t\t\tretval = setsockopt(session->rtp.gs.socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,\n\t\t\t                    (SOCKET_OPTION_VALUE)&session->multicast_ttl, sizeof(session->multicast_ttl));\n\n\t\t\tif (retval < 0) break;\n\n\t\t\tretval = setsockopt(session->rtcp.gs.socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,\n\t\t\t                    (SOCKET_OPTION_VALUE)&session->multicast_ttl, sizeof(session->multicast_ttl));\n\t\t} break;\n\t\tdefault:\n\t\t\tretval = -1;\n\t}\n\n\tif (retval < 0) ortp_warning(\"Failed to set multicast TTL on socket.\");\n\n\treturn retval;\n}\n\n/**\n *rtp_session_get_multicast_ttl:\n *@param session: a rtp session\n *\n * Returns the TTL (Time-To-Live) for outgoing multicast packets.\n *\n **/\nint rtp_session_get_multicast_ttl(RtpSession *session) {\n\treturn session->multicast_ttl;\n}\n\n/**\n *@param session: a rtp session\n *@param yesno: enable multicast loopback\n *\n * Enable multicast loopback.\n *\n * Returns: 0 on success.\n *\n **/\nint rtp_session_set_multicast_loopback(RtpSession *session, int yesno) {\n\tint retval;\n\n\t// Store new loopback state if one is specified\n\tif (yesno == 0) {\n\t\t// Don't loop back\n\t\tsession->multicast_loopback = 0;\n\t} else if (yesno > 0) {\n\t\t// Do loop back\n\t\tsession->multicast_loopback = 1;\n\t}\n\n\t// Don't do anything if socket hasn't been created yet\n\tif (session->rtp.gs.socket == (ortp_socket_t)-1) return 0;\n\n\tswitch (session->rtp.gs.sockfamily) {\n\t\tcase AF_INET: {\n\n\t\t\tretval = setsockopt(session->rtp.gs.socket, IPPROTO_IP, IP_MULTICAST_LOOP,\n\t\t\t                    (SOCKET_OPTION_VALUE)&session->multicast_loopback, sizeof(session->multicast_loopback));\n\n\t\t\tif (retval < 0) break;\n\n\t\t\tretval = setsockopt(session->rtcp.gs.socket, IPPROTO_IP, IP_MULTICAST_LOOP,\n\t\t\t                    (SOCKET_OPTION_VALUE)&session->multicast_loopback, sizeof(session->multicast_loopback));\n\n\t\t} break;\n\t\tcase AF_INET6: {\n\n\t\t\tretval = setsockopt(session->rtp.gs.socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,\n\t\t\t                    (SOCKET_OPTION_VALUE)&session->multicast_loopback, sizeof(session->multicast_loopback));\n\n\t\t\tif (retval < 0) break;\n\n\t\t\tretval = setsockopt(session->rtcp.gs.socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,\n\t\t\t                    (SOCKET_OPTION_VALUE)&session->multicast_loopback, sizeof(session->multicast_loopback));\n\t\t} break;\n\t\tdefault:\n\t\t\tretval = -1;\n\t}\n\n\tif (retval < 0) ortp_warning(\"Failed to set multicast loopback on socket.\");\n\n\treturn retval;\n}\n\n/**\n *rtp_session_get_multicast_loopback:\n *@param session: a rtp session\n *\n * Returns the multicast loopback state of rtp session (true or false).\n *\n **/\nint rtp_session_get_multicast_loopback(RtpSession *session) {\n\treturn session->multicast_loopback;\n}\n\n/**\n *rtp_session_set_dscp:\n *@param session: a rtp session\n *@param dscp: desired DSCP PHB value\n *\n * Sets the DSCP (Differentiated Services Code Point) for outgoing RTP packets.\n *\n * Returns: 0 on success.\n *\n **/\nint rtp_session_set_dscp(RtpSession *session, int dscp) {\n\tint retval = 0;\n\tint tos;\n\tint proto;\n\tint value_type;\n\n\t// Store new DSCP value if one is specified\n\tif (dscp >= 0) session->dscp = dscp;\n\n\t// Don't do anything if socket hasn't been created yet\n\tif (session->rtp.gs.socket == (ortp_socket_t)-1) return 0;\n\n#if (_WIN32_WINNT >= 0x0600) && defined(ORTP_WINDOWS_DESKTOP)\n#if !defined(ENABLE_MICROSOFT_STORE_APP) && !defined(ORTP_WINDOWS_UWP)\n\tortp_message(\"check OS support for qwave.lib\");\n\tif (IsWindowsVistaOrGreater()) {\n\t\tif (session->dscp == 0) tos = QOSTrafficTypeBestEffort;\n\t\telse if (session->dscp == 0x8) tos = QOSTrafficTypeBackground;\n\t\telse if (session->dscp == 0x28) tos = QOSTrafficTypeAudioVideo;\n\t\telse if (session->dscp == 0x38) tos = QOSTrafficTypeVoice;\n\t\telse tos = QOSTrafficTypeExcellentEffort; /* 0x28 */\n\n\t\tif (session->rtp.QoSHandle == NULL) {\n\t\t\tQOS_VERSION version;\n\t\t\tBOOL QoSResult;\n\n\t\t\tversion.MajorVersion = 1;\n\t\t\tversion.MinorVersion = 0;\n\n\t\t\tQoSResult = QOSCreateHandle(&version, &session->rtp.QoSHandle);\n\n\t\t\tif (QoSResult != TRUE) {\n\t\t\t\tortp_error(\"QOSCreateHandle failed to create handle with error %d\", GetLastError());\n\t\t\t\tretval = -1;\n\t\t\t}\n\t\t}\n\t\tif (session->rtp.QoSHandle != NULL) {\n\t\t\tBOOL QoSResult;\n\t\t\tbool_t socket_connected = session->flags & RTP_SOCKET_CONNECTED;\n\t\t\t// If there is no address destination QOSAddSocketToFlow cannot be used unless it is already connected\n\t\t\t// (which become optional). As the remote address is only known after being connected, it is preferred to\n\t\t\t// let the API to do its stuff by not specifying any address.\n\t\t\tif (socket_connected) {\n\t\t\t\tQoSResult = QOSAddSocketToFlow(session->rtp.QoSHandle, session->rtp.gs.socket, NULL, tos,\n\t\t\t\t                               QOS_NON_ADAPTIVE_FLOW, &session->rtp.QoSFlowID);\n\t\t\t\tif (QoSResult != TRUE) {\n\t\t\t\t\tortp_error(\"QOSAddSocketToFlow failed to add a flow with error %d\", GetLastError());\n\t\t\t\t\tretval = -1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n#endif // ENABLE_MICROSOFT_STORE_APP\n#endif\n\t\t// DSCP value is in the upper six bits of the TOS field\n\t\ttos = (session->dscp << 2) & 0xFC;\n\t\tswitch (session->rtp.gs.sockfamily) {\n\t\t\tcase AF_INET:\n\t\t\t\tproto = IPPROTO_IP;\n\t\t\t\tvalue_type = IP_TOS;\n\t\t\t\tbreak;\n\t\t\tcase AF_INET6:\n\t\t\t\tproto = IPPROTO_IPV6;\n#ifdef IPV6_TCLASS /*seems not defined by my libc*/\n\t\t\t\tvalue_type = IPV6_TCLASS;\n#else\n\t\t\tvalue_type = IP_TOS;\n#endif\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tortp_error(\"Cannot set DSCP because socket family is unspecified.\");\n\t\t\t\treturn -1;\n\t\t}\n\t\tretval = setsockopt(session->rtp.gs.socket, proto, value_type, (SOCKET_OPTION_VALUE)&tos, sizeof(tos));\n\t\tif (retval == -1) ortp_error(\"Fail to set DSCP value on rtp socket: %s\", getSocketError());\n\t\tif (session->rtcp.gs.socket != (ortp_socket_t)-1) {\n\t\t\tif (setsockopt(session->rtcp.gs.socket, proto, value_type, (SOCKET_OPTION_VALUE)&tos, sizeof(tos)) == -1) {\n\t\t\t\tortp_error(\"Fail to set DSCP value on rtcp socket: %s\", getSocketError());\n\t\t\t}\n\t\t}\n#if (_WIN32_WINNT >= 0x0600) && defined(ORTP_WINDOWS_DESKTOP)\n#if !defined(ENABLE_MICROSOFT_STORE_APP) && !defined(ORTP_WINDOWS_UWP)\n\t}\n#endif // ENABLE_MICROSOFT_STORE_APP\n#endif\n\treturn retval;\n}\n\n/**\n *rtp_session_get_dscp:\n *@param session: a rtp session\n *\n * Returns the DSCP (Differentiated Services Code Point) for outgoing RTP packets.\n *\n **/\nint rtp_session_get_dscp(const RtpSession *session) {\n\treturn session->dscp;\n}\n\n/**\n *rtp_session_get_local_port:\n *@param session:\ta rtp session for which rtp_session_set_local_addr() or rtp_session_set_remote_addr() has been\n *called\n *\n *\tThis function can be useful to retrieve the local port that was randomly choosen by\n *\trtp_session_set_remote_addr() when rtp_session_set_local_addr() was not called.\n *\n *\tReturns: the local port used to listen for rtp packets, -1 if not set.\n **/\n\nint rtp_session_get_local_port(const RtpSession *session) {\n\treturn (session->rtp.gs.loc_port > 0) ? session->rtp.gs.loc_port : -1;\n}\n\nint rtp_session_get_local_rtcp_port(const RtpSession *session) {\n\treturn (session->rtcp.gs.loc_port > 0) ? session->rtcp.gs.loc_port : -1;\n}\n\n/**\n *rtp_session_set_remote_addr:\n *@param session:\t\ta rtp session freshly created.\n *@param addr:\t\ta remote IP address in the xxx.xxx.xxx.xxx form.\n *@param port:\t\ta remote port.\n *\n *\tSets the remote address of the rtp session, ie the destination address where rtp packet\n *\tare sent. If the session is recv-only or duplex, it also sets the origin of incoming RTP\n *\tpackets. Rtp packets that don't come from addr:port are discarded.\n *\n *\tReturns: 0 on success.\n **/\nint rtp_session_set_remote_addr(RtpSession *session, const char *addr, int port) {\n\treturn rtp_session_set_remote_addr_full(session, addr, port, addr, port + 1);\n}\n\n/**\n *rtp_session_set_remote_addr_full:\n *@param session:\t\ta rtp session freshly created.\n *@param rtp_addr:\t\ta remote IP address in the xxx.xxx.xxx.xxx form.\n *@param rtp_port:\t\ta remote rtp port.\n *@param rtcp_addr:\t\ta remote IP address in the xxx.xxx.xxx.xxx form.\n *@param rtcp_port:\t\ta remote rtcp port.\n *\n *\tSets the remote address of the rtp session, ie the destination address where rtp packet\n *\tare sent. If the session is recv-only or duplex, it also sets the origin of incoming RTP\n *\tpackets. Rtp packets that don't come from addr:port are discarded.\n *\n *\tReturns: 0 on success.\n **/\n\nint rtp_session_set_remote_addr_full(\n    RtpSession *session, const char *rtp_addr, int rtp_port, const char *rtcp_addr, int rtcp_port) {\n\treturn _rtp_session_set_remote_addr_full(session, rtp_addr, rtp_port, rtcp_addr, rtcp_port, FALSE);\n}\n\nstatic int _rtp_session_set_remote_addr_full(\n    RtpSession *session, const char *rtp_addr, int rtp_port, const char *rtcp_addr, int rtcp_port, bool_t is_aux) {\n\tchar rtp_printable_addr[64];\n\tchar rtcp_printable_addr[64];\n\tint err;\n\tstruct addrinfo *res0, *res;\n\tstruct sockaddr_storage *rtp_saddr = &session->rtp.gs.rem_addr;\n\tsocklen_t *rtp_saddr_len = &session->rtp.gs.rem_addrlen;\n\tstruct sockaddr_storage *rtp_saddr_prev = &session->rtp.gs.rem_addr_previously_set;\n\tsocklen_t *rtp_saddr_prev_len = &session->rtp.gs.rem_addr_previously_set_len;\n\tstruct sockaddr_storage *rtcp_saddr = &session->rtcp.gs.rem_addr;\n\tsocklen_t *rtcp_saddr_len = &session->rtcp.gs.rem_addrlen;\n\tstruct sockaddr_storage *rtcp_saddr_prev = &session->rtcp.gs.rem_addr_previously_set;\n\tsocklen_t *rtcp_saddr_prev_len = &session->rtcp.gs.rem_addr_previously_set_len;\n\tOrtpAddress *aux_rtp = NULL, *aux_rtcp = NULL;\n\n\tif (is_aux) {\n\t\taux_rtp = ortp_malloc0(sizeof(OrtpAddress));\n\t\trtp_saddr = &aux_rtp->addr;\n\t\trtp_saddr_len = &aux_rtp->len;\n\t\taux_rtcp = ortp_malloc0(sizeof(OrtpAddress));\n\t\trtcp_saddr = &aux_rtcp->addr;\n\t\trtcp_saddr_len = &aux_rtcp->len;\n\t}\n\n\tres0 = bctbx_name_to_addrinfo((session->rtp.gs.socket == -1) ? AF_UNSPEC : session->rtp.gs.sockfamily, SOCK_DGRAM,\n\t                              rtp_addr, rtp_port);\n\tif (res0 == NULL) {\n\t\tortp_error(\"_rtp_session_set_remote_addr_full(): cannot set RTP destination to %s port %i.\", rtp_addr,\n\t\t           rtp_port);\n\t\terr = -1;\n\t\tgoto end;\n\t} else {\n\t\tbctbx_addrinfo_to_printable_ip_address(res0, rtp_printable_addr, sizeof(rtp_printable_addr));\n\t}\n\tif (session->rtp.gs.socket == -1) {\n\t\t/* the session has not its socket bound, do it */\n\t\tortp_message(\"Setting random local addresses.\");\n\t\t/* bind to an address type that matches the destination address */\n\t\tif (res0->ai_addr->sa_family == AF_INET6) err = rtp_session_set_local_addr(session, \"::\", -1, -1);\n\t\telse err = rtp_session_set_local_addr(session, \"0.0.0.0\", -1, -1);\n\t\tif (err < 0) {\n\t\t\terr = -1;\n\t\t\tgoto end;\n\t\t}\n\t}\n\n\terr = -1;\n\tfor (res = res0; res; res = res->ai_next) {\n\t\t/* set a destination address that has the same type as the local address */\n\t\tif (res->ai_family == session->rtp.gs.sockfamily) {\n\t\t\tif (session->rtp.gs.remote_address_adaptation == FALSE && rtp_session_get_symmetric_rtp(session) &&\n\t\t\t    memcmp(rtp_saddr_prev, res->ai_addr, res->ai_addrlen) == 0) {\n\t\t\t\tsession->rtp.gs.remote_address_adaptation = TRUE;\n\t\t\t\tortp_message(\"RtpSession[%p]: Not changing RTP stream's destination address because it is the same as \"\n\t\t\t\t             \"the one previously set and we are in symmetric RTP mode.\",\n\t\t\t\t             session);\n\t\t\t} else {\n\t\t\t\tmemcpy(rtp_saddr, res->ai_addr, res->ai_addrlen);\n\t\t\t\t*rtp_saddr_len = (socklen_t)res->ai_addrlen;\n\t\t\t\tif (*rtp_saddr_prev_len == 0) {\n\t\t\t\t\tmemcpy(rtp_saddr_prev, res->ai_addr, res->ai_addrlen);\n\t\t\t\t\t*rtp_saddr_prev_len = (socklen_t)res->ai_addrlen;\n\t\t\t\t} else {\n\t\t\t\t\tsession->rtp.gs.remote_address_adaptation = FALSE;\n\t\t\t\t}\n\t\t\t}\n\t\t\terr = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\tbctbx_freeaddrinfo(res0);\n\tif (err) {\n\t\tortp_warning(\"Could not set destination for RTP socket to %s:%i.\", rtp_addr, rtp_port);\n\t\tgoto end;\n\t}\n\n\tif ((rtcp_addr != NULL) && (rtcp_port > 0)) {\n\t\tres0 = bctbx_name_to_addrinfo((session->rtcp.gs.socket == -1) ? AF_UNSPEC : session->rtcp.gs.sockfamily,\n\t\t                              SOCK_DGRAM, rtcp_addr, rtcp_port);\n\t\tif (res0 == NULL) {\n\t\t\tortp_error(\"_rtp_session_set_remote_addr_full(): cannot set RTCP destination to %s port %i.\", rtcp_addr,\n\t\t\t           rtcp_port);\n\t\t\terr = -1;\n\t\t\tgoto end;\n\t\t} else {\n\t\t\tbctbx_addrinfo_to_printable_ip_address(res0, rtcp_printable_addr, sizeof(rtcp_printable_addr));\n\t\t}\n\t\terr = -1;\n\t\tfor (res = res0; res; res = res->ai_next) {\n\t\t\t/* set a destination address that has the same type as the local address */\n\t\t\tif (res->ai_family == session->rtcp.gs.sockfamily) {\n\t\t\t\tif (session->rtcp.gs.remote_address_adaptation == FALSE && rtp_session_get_symmetric_rtp(session) &&\n\t\t\t\t    memcmp(rtcp_saddr_prev, res->ai_addr, res->ai_addrlen) == 0) {\n\t\t\t\t\tsession->rtcp.gs.remote_address_adaptation = TRUE;\n\t\t\t\t\tbctbx_freeaddrinfo(res0);\n\t\t\t\t\tgoto end;\n\t\t\t\t} else {\n\t\t\t\t\tif (*rtcp_saddr_prev_len == 0) {\n\t\t\t\t\t\tmemcpy(rtcp_saddr_prev, res->ai_addr, res->ai_addrlen);\n\t\t\t\t\t\t*rtcp_saddr_prev_len = (socklen_t)res->ai_addrlen;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsession->rtcp.gs.remote_address_adaptation = FALSE;\n\t\t\t\t\t}\n\t\t\t\t\tmemcpy(rtcp_saddr, res->ai_addr, res->ai_addrlen);\n\t\t\t\t\t*rtcp_saddr_len = (socklen_t)res->ai_addrlen;\n\t\t\t\t}\n\t\t\t\terr = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tbctbx_freeaddrinfo(res0);\n\t\tif (err) {\n\t\t\tortp_warning(\"Could not set destination for RCTP socket to %s:%i.\", rtcp_addr, rtcp_port);\n\t\t\tgoto end;\n\t\t}\n\n\t\tif (can_connect(session)) {\n\t\t\tif (try_connect(session->rtp.gs.socket, (struct sockaddr *)&session->rtp.gs.rem_addr,\n\t\t\t                session->rtp.gs.rem_addrlen))\n\t\t\t\tsession->flags |= RTP_SOCKET_CONNECTED;\n\t\t\tif (session->rtcp.gs.socket != (ortp_socket_t)-1) {\n\t\t\t\tif (try_connect(session->rtcp.gs.socket, (struct sockaddr *)&session->rtcp.gs.rem_addr,\n\t\t\t\t                session->rtcp.gs.rem_addrlen))\n\t\t\t\t\tsession->flags |= RTCP_SOCKET_CONNECTED;\n\t\t\t}\n\t\t} else if (session->flags & RTP_SOCKET_CONNECTED) {\n\t\t\t/*must dissolve association done by connect().\n\t\t\tSee connect(2) manpage*/\n\t\t\tstruct sockaddr sa;\n\t\t\tsa.sa_family = AF_UNSPEC;\n\t\t\tif (connect(session->rtp.gs.socket, &sa, sizeof(sa)) < 0) {\n\t\t\t\tortp_error(\"Cannot dissolve connect() association for rtp socket: %s\", getSocketError());\n\t\t\t}\n\t\t\tif (connect(session->rtcp.gs.socket, &sa, sizeof(sa)) < 0) {\n\t\t\t\tortp_error(\"Cannot dissolve connect() association for rtcp socket: %s\", getSocketError());\n\t\t\t}\n\t\t\tsession->flags &= ~RTP_SOCKET_CONNECTED;\n\t\t\tsession->flags &= ~RTCP_SOCKET_CONNECTED;\n\t\t}\n\n\t\tortp_message(\"RtpSession [%p] sending to rtp %s rtcp %s %s\", session, rtp_printable_addr, rtcp_printable_addr,\n\t\t             is_aux ? \"as auxiliary destination\" : \"\");\n\t} else {\n\t\tortp_message(\"RtpSession [%p] sending to rtp %s %s\", session, rtp_printable_addr,\n\t\t             is_aux ? \"as auxiliary destination\" : \"\");\n\t}\n\t/*Apply DSCP setting. On windows the destination address is required for doing this.*/\n\trtp_session_set_dscp(session, -1);\nend:\n\tif (is_aux) {\n\t\tif (err == -1) {\n\t\t\tortp_free(aux_rtp);\n\t\t\tortp_free(aux_rtcp);\n\t\t} else {\n\t\t\tortp_mutex_lock(&session->main_mutex);\n\t\t\tsession->rtp.gs.aux_destinations = o_list_append(session->rtp.gs.aux_destinations, aux_rtp);\n\t\t\tsession->rtcp.gs.aux_destinations = o_list_append(session->rtcp.gs.aux_destinations, aux_rtcp);\n\t\t\tortp_mutex_unlock(&session->main_mutex);\n\t\t}\n\t}\n\treturn err;\n}\n\nint rtp_session_set_remote_addr_and_port(RtpSession *session, const char *addr, int rtp_port, int rtcp_port) {\n\treturn rtp_session_set_remote_addr_full(session, addr, rtp_port, addr, rtcp_port);\n}\n\n/**\n *rtp_session_add_remote_aux_addr_full:\n *@param session:\t\ta rtp session freshly created.\n *@param rtp_addr:\t\ta local IP address in the xxx.xxx.xxx.xxx form.\n *@param rtp_port:\t\ta local rtp port.\n *@param rtcp_addr:\t\ta local IP address in the xxx.xxx.xxx.xxx form.\n *@param rtcp_port:\t\ta local rtcp port.\n *\n *\tAdd an auxiliary remote address for the rtp session, ie a destination address where rtp packet\n *\tare sent.\n *\n *\tReturns: 0 on success.\n **/\n\nint rtp_session_add_aux_remote_addr_full(\n    RtpSession *session, const char *rtp_addr, int rtp_port, const char *rtcp_addr, int rtcp_port) {\n\treturn _rtp_session_set_remote_addr_full(session, rtp_addr, rtp_port, rtcp_addr, rtcp_port, TRUE);\n}\n\nvoid rtp_session_clear_aux_remote_addr(RtpSession *session) {\n\tortp_mutex_lock(&session->main_mutex);\n\tortp_stream_clear_aux_addresses(&session->rtp.gs);\n\tortp_stream_clear_aux_addresses(&session->rtcp.gs);\n\tortp_mutex_unlock(&session->main_mutex);\n}\n\nvoid rtp_session_set_sockets(RtpSession *session, int rtpfd, int rtcpfd) {\n\tif (rtpfd != -1) set_non_blocking_socket(rtpfd);\n\tif (rtcpfd != -1) set_non_blocking_socket(rtcpfd);\n\tsession->rtp.gs.socket = rtpfd;\n\tsession->rtcp.gs.socket = rtcpfd;\n\tif (rtpfd != -1 || rtcpfd != -1)\n\t\tsession->flags |= (RTP_SESSION_USING_EXT_SOCKETS | RTP_SOCKET_CONNECTED | RTCP_SOCKET_CONNECTED);\n\telse session->flags &= ~(RTP_SESSION_USING_EXT_SOCKETS | RTP_SOCKET_CONNECTED | RTCP_SOCKET_CONNECTED);\n}\n\nvoid rtp_session_set_transports(RtpSession *session, struct _RtpTransport *rtptr, struct _RtpTransport *rtcptr) {\n\tsession->rtp.gs.tr = rtptr;\n\tsession->rtcp.gs.tr = rtcptr;\n\tif (rtptr) rtptr->session = session;\n\tif (rtcptr) rtcptr->session = session;\n\n\tif (rtptr || rtcptr) session->flags |= (RTP_SESSION_USING_TRANSPORT);\n\telse session->flags &= ~(RTP_SESSION_USING_TRANSPORT);\n}\n\nvoid rtp_session_get_transports(const RtpSession *session, RtpTransport **rtptr, RtpTransport **rtcptr) {\n\tif (rtptr) *rtptr = session->rtp.gs.tr;\n\tif (rtcptr) *rtcptr = session->rtcp.gs.tr;\n}\n\n/**\n *rtp_session_flush_sockets:\n *@param session: a rtp session\n *\n * Flushes the sockets for all pending incoming packets.\n * This can be usefull if you did not listen to the stream for a while\n * and wishes to start to receive again. During the time no receive is made\n * packets get bufferised into the internal kernel socket structure.\n *\n **/\nvoid rtp_session_flush_sockets(RtpSession *session) {\n\trtp_session_set_flag(session, RTP_SESSION_FLUSH);\n\trtp_session_rtp_recv(session, 0);\n\trtp_session_unset_flag(session, RTP_SESSION_FLUSH);\n}\n\n#if defined(_WIN32) || defined(_WIN32_WCE)\n#define MAX_BUF 64\nstatic int rtp_sendmsg(ortp_socket_t sock, mblk_t *m, const struct sockaddr *rem_addr, socklen_t addr_len) {\n\tWSAMSG msg = {0};\n\tWSABUF wsabuf[MAX_BUF];\n\tDWORD dwBytes = 0;\n\tCHAR ctrlBuffer[512] = {0};\n\tint error;\n\tint controlSize = 0;\n\tint wsabufLen;\n\tmblk_t *mTrack = m;\n\tPWSACMSGHDR cmsg = NULL;\n\tstruct sockaddr_storage v4, v6Mapped;\n\tsocklen_t v4Len = 0, v6MappedLen = 0;\n\tbool_t useV4 = FALSE;\n\tstruct sockaddr remote_address = {0};\n\tstruct addrinfo *feed_server = NULL;\n\n\tfor (wsabufLen = 0; wsabufLen < MAX_BUF && mTrack != NULL; mTrack = mTrack->b_cont, ++wsabufLen) {\n\t\twsabuf[wsabufLen].len = (ULONG)(mTrack->b_wptr - mTrack->b_rptr);\n\t\twsabuf[wsabufLen].buf = (CHAR *)mTrack->b_rptr;\n\t}\n\tmsg.lpBuffers = wsabuf; // contents\n\tmsg.dwBufferCount = wsabufLen;\n\tif (wsabufLen == MAX_BUF) {\n\t\tint count = 0;\n\t\twhile (mTrack != NULL) {\n\t\t\t++count;\n\t\t\tmTrack = mTrack->b_cont;\n\t\t}\n\t\tortp_error(\"Too long msgb (%i fragments) , didn't fit into iov, end discarded.\", MAX_BUF + count);\n\t}\n\tif (addr_len == 0) {\n\t\t// Get the local host information\n\t\tmsg.namelen = 0;\n\t\tmsg.name = NULL;\n\t} else {\n\t\tmsg.namelen = addr_len;\n\t\tmsg.name = (SOCKADDR *)rem_addr;\n\t}\n\tmsg.Control.buf = ctrlBuffer;\n\tmsg.Control.len = sizeof(ctrlBuffer);\n\tcmsg = WSA_CMSG_FIRSTHDR(&msg);\n#ifdef IPV6_PKTINFO\n\tif (m->recv_addr.family == AF_INET6 && !IN6_IS_ADDR_UNSPECIFIED(&m->recv_addr.addr.ipi6_addr) &&\n\t    !IN6_IS_ADDR_LOOPBACK(&m->recv_addr.addr.ipi6_addr)) {\n\t\tif (IN6_IS_ADDR_V4MAPPED(&m->recv_addr.addr.ipi6_addr)) {\n\t\t\tuseV4 = TRUE;\n\t\t\tortp_recvaddr_to_sockaddr(&m->recv_addr, (struct sockaddr *)&v6Mapped, &v6MappedLen);\n\t\t\tbctbx_sockaddr_remove_v4_mapping((struct sockaddr *)&v6Mapped, (struct sockaddr *)&v4, &v4Len);\n\t\t} else {\n\t\t\tPIN6_PKTINFO pPktInfo = NULL;\n\t\t\tcmsg->cmsg_len = WSA_CMSG_LEN(sizeof(IN6_PKTINFO));\n\t\t\tcmsg->cmsg_level = IPPROTO_IPV6;\n\t\t\tcmsg->cmsg_type = IPV6_PKTINFO;\n\t\t\tpPktInfo = (PIN6_PKTINFO)WSA_CMSG_DATA(cmsg);\n\t\t\tpPktInfo->ipi6_addr = m->recv_addr.addr.ipi6_addr;\n\t\t\tcontrolSize += WSA_CMSG_SPACE(sizeof(IN6_PKTINFO));\n\t\t\tcmsg = WSA_CMSG_NXTHDR(&msg, cmsg);\n\t\t}\n\t}\n#endif\n#ifdef IP_PKTINFO\n\tif (m->recv_addr.family == AF_INET || useV4 == TRUE) { // Add IPV4 to the message control\n\t\tPIN_PKTINFO pPktInfo = NULL;\n\t\tcmsg->cmsg_len = WSA_CMSG_LEN(sizeof(IN_PKTINFO));\n\t\tcmsg->cmsg_level = IPPROTO_IP;\n\t\tcmsg->cmsg_type = IP_PKTINFO;\n\t\tpPktInfo = (PIN_PKTINFO)WSA_CMSG_DATA(cmsg);\n\t\tif (useV4 == TRUE) pPktInfo->ipi_addr = ((SOCKADDR_IN *)&v4)->sin_addr;\n\t\telse pPktInfo->ipi_addr = m->recv_addr.addr.ipi_addr;\n\t\tcontrolSize += WSA_CMSG_SPACE(sizeof(IN_PKTINFO));\n\t}\n#endif\n\tmsg.Control.len = controlSize;\n\tif (controlSize == 0) msg.Control.buf = NULL;\n\terror = WSASendMsg((SOCKET)sock, &msg, 0, &dwBytes, NULL, NULL);\n\tif (error == SOCKET_ERROR && controlSize != 0) { // Debug note : if unexpected WSAEFAULT, check the location of\n\t\t                                             // memory allocation of msg. It must be on ortp addresses space.\n\t\tint errorCode = WSAGetLastError();\n\t\tif (errorCode == WSAEINVAL || errorCode == WSAENETUNREACH || errorCode == WSAEFAULT) {\n\t\t\tmsg.Control.len = 0;\n\t\t\tmsg.Control.buf = NULL;\n\t\t\terror = WSASendMsg((SOCKET)sock, &msg, 0, &dwBytes, NULL, NULL);\n\t\t}\n\t}\n\tmTrack = m;\n\tif (error >= 0) return dwBytes; // Return the bytes that have been sent\n\telse return error;\n}\n#else\n#ifdef USE_SENDMSG\n#define MAX_IOV 64\nstatic int rtp_sendmsg(ortp_socket_t sock, mblk_t *m, const struct sockaddr *rem_addr, socklen_t addr_len) {\n\tstruct msghdr msg;\n\tstruct iovec iov[MAX_IOV];\n\tint iovlen;\n\tu_char control_buffer[512] = {0};\n\tmblk_t *m_track = m;\n\tint controlSize = 0; // Used to reset msg.msg_controllen to the real control size\n\tstruct cmsghdr *cmsg;\n\tstruct sockaddr_storage v4, v6Mapped;\n\tsocklen_t v4Len = 0, v6MappedLen = 0;\n\tbool_t useV4 = FALSE;\n\tint error;\n\n\tfor (iovlen = 0; iovlen < MAX_IOV && m_track != NULL; m_track = m_track->b_cont, iovlen++) {\n\t\tiov[iovlen].iov_base = m_track->b_rptr;\n\t\tiov[iovlen].iov_len = m_track->b_wptr - m_track->b_rptr;\n\t}\n\tif (iovlen == MAX_IOV) {\n\t\tint count = 0;\n\t\twhile (m_track != NULL) {\n\t\t\tcount++;\n\t\t\tm_track = m_track->b_cont;\n\t\t}\n\t\tortp_error(\"Too long msgb (%i fragments) , didn't fit into iov, end discarded.\", MAX_IOV + count);\n\t}\n\tmsg.msg_name = (void *)rem_addr;\n\tmsg.msg_namelen = addr_len;\n\tmsg.msg_iov = &iov[0];\n\tmsg.msg_iovlen = iovlen;\n\tmsg.msg_flags = 0;\n\tmsg.msg_control = control_buffer;\n\tmsg.msg_controllen = sizeof(control_buffer);\n\n\tcmsg = CMSG_FIRSTHDR(&msg);\n#ifdef IPV6_PKTINFO\n\tif (m->recv_addr.family == AF_INET6 && !IN6_IS_ADDR_UNSPECIFIED(&m->recv_addr.addr.ipi6_addr) &&\n\t    !IN6_IS_ADDR_LOOPBACK(&m->recv_addr.addr.ipi6_addr)) { // Add IPV6 to the message control. We only add it if the\n\t\t                                                       // IP is specified and is not link local\n\t\tif (IN6_IS_ADDR_V4MAPPED(&m->recv_addr.addr.ipi6_addr)) {\n\t\t\tuseV4 = TRUE;\n\t\t\tortp_recvaddr_to_sockaddr(&m->recv_addr, (struct sockaddr *)&v6Mapped, &v6MappedLen);\n\t\t\tbctbx_sockaddr_remove_v4_mapping((struct sockaddr *)&v6Mapped, (struct sockaddr *)&v4, &v4Len);\n\t\t} else {\n\t\t\tstruct in6_pktinfo *pktinfo;\n\t\t\tcmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));\n\t\t\tcmsg->cmsg_level = IPPROTO_IPV6;\n\t\t\tcmsg->cmsg_type = IPV6_PKTINFO;\n\t\t\tpktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);\n\t\t\tpktinfo->ipi6_ifindex = 0; // Set to 0 to let the kernel to use routable interface\n\t\t\tpktinfo->ipi6_addr = m->recv_addr.addr.ipi6_addr;\n\t\t\tcontrolSize += CMSG_SPACE(sizeof(struct in6_pktinfo));\n\t\t\tcmsg = CMSG_NXTHDR(&msg, cmsg);\n\t\t}\n\t}\n#endif\n#ifdef IP_PKTINFO\n\tif (m->recv_addr.family == AF_INET || useV4 == TRUE) { // Add IPV4 to the message control\n\t\tstruct in_pktinfo *pktinfo;\n\t\tcmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));\n\t\tcmsg->cmsg_level = IPPROTO_IP;\n\t\tcmsg->cmsg_type = IP_PKTINFO;\n\t\tpktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);\n\t\tif (useV4 == TRUE) pktinfo->ipi_spec_dst = ((struct sockaddr_in *)&v4)->sin_addr;\n\t\telse pktinfo->ipi_spec_dst = m->recv_addr.addr.ipi_addr;\n\t\tcontrolSize += CMSG_SPACE(sizeof(struct in_pktinfo));\n\t\tcmsg = CMSG_NXTHDR(&msg, cmsg);\n\t}\n#endif\n\n#ifdef IPV6_RECVDSTADDR\n\tif (m->recv_addr.family == AF_INET6 && !IN6_IS_ADDR_UNSPECIFIED(&m->recv_addr.addr.ipi6_addr) &&\n\t    !IN6_IS_ADDR_LOOPBACK(&m->recv_addr.addr.ipi6_addr)) { // Add IPV6 to the message control. We only add it if the\n\t\t                                                       // IP is specified and is not link local\n\t\tif (IN6_IS_ADDR_V4MAPPED(&m->recv_addr.addr.ipi6_addr)) {\n\t\t\tuseV4 = TRUE;\n\t\t\tortp_recvaddr_to_sockaddr(&m->recv_addr, (struct sockaddr *)&v6Mapped, &v6MappedLen);\n\t\t\tbctbx_sockaddr_remove_v4_mapping((struct sockaddr *)&v6Mapped, (struct sockaddr *)&v4, &v4Len);\n\t\t} else {\n\t\t\tstruct in6_addr *pktinfo;\n\t\t\tcmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_addr));\n\t\t\tcmsg->cmsg_level = IPPROTO_IPV6;\n\t\t\tcmsg->cmsg_type = IPV6_RECVDSTADDR;\n\t\t\tpktinfo = (struct in6_addr *)CMSG_DATA(cmsg);\n\t\t\t*pktinfo = m->recv_addr.addr.ipi6_addr;\n\t\t\tcontrolSize += CMSG_SPACE(sizeof(struct in6_addr));\n\t\t\tcmsg = CMSG_NXTHDR(&msg, cmsg);\n\t\t}\n\t}\n#endif\n#if defined(IP_RECVDSTADDR)\n\tif (m->recv_addr.family == AF_INET || useV4 == TRUE) { // Add IPV4 to the message control\n\t\tstruct in_addr *pktinfo;\n\t\tcmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));\n\t\tcmsg->cmsg_level = IPPROTO_IP;\n\t\tcmsg->cmsg_type = IP_RECVDSTADDR;\n\t\tpktinfo = (struct in_addr *)CMSG_DATA(cmsg);\n\t\tif (useV4 == TRUE) *pktinfo = ((struct sockaddr_in *)&v4)->sin_addr;\n\t\telse *pktinfo = m->recv_addr.addr.ipi_addr;\n\t\tcontrolSize += CMSG_SPACE(sizeof(struct in_addr));\n\t\t//\t\tcmsg = CMSG_NXTHDR(&msg, cmsg);\t\t// Uncomment if you want to add interfaces\n\t}\n#endif\n\n\tmsg.msg_controllen = controlSize;\n\tif (controlSize == 0) // Have to reset msg_control to NULL as msg_controllen is not sufficient on some platforms\n\t\tmsg.msg_control = NULL;\n\terror = sendmsg((int)sock, &msg, 0);\n\tif (error == -1 && controlSize != 0 && (errno == EINVAL || errno == ENETUNREACH || errno == EFAULT)) {\n\t\tmsg.msg_controllen = 0;\n\t\tmsg.msg_control = NULL;\n\t\terror = sendmsg((int)sock, &msg, 0);\n\t}\n\treturn error;\n}\n#endif\n#endif\n\nortp_socket_t rtp_session_get_socket(RtpSession *session, bool_t is_rtp) {\n\treturn is_rtp ? session->rtp.gs.socket : session->rtcp.gs.socket;\n}\n\nint _ortp_sendto(\n    ortp_socket_t sockfd, mblk_t *m, BCTBX_UNUSED(int flags), const struct sockaddr *destaddr, socklen_t destlen) {\n\tint sent_bytes;\n#if defined(_WIN32) || defined(_WIN32_WCE) || defined(USE_SENDMSG)\n\tsent_bytes = rtp_sendmsg(sockfd, m, destaddr, destlen);\n#else\n\tif (m->b_cont != NULL) msgpullup(m, -1);\n\tsent_bytes = sendto(sockfd, (char *)m->b_rptr, (int)(m->b_wptr - m->b_rptr), 0, destaddr, destlen);\n#endif\n\treturn sent_bytes;\n}\n\nint rtp_session_sendto(\n    RtpSession *session, bool_t is_rtp, mblk_t *m, int flags, const struct sockaddr *destaddr, socklen_t destlen) {\n\tint ret = 0;\n\tint using_simulator = FALSE;\n\t_rtp_session_check_socket_refresh(session);\n\n\tif (session->net_sim_ctx) {\n\t\tortp_mutex_lock(&session->main_mutex);\n\t\tif (session->net_sim_ctx && (session->net_sim_ctx->params.mode == OrtpNetworkSimulatorOutbound ||\n\t\t                             session->net_sim_ctx->params.mode == OrtpNetworkSimulatorOutboundControlled)) {\n\t\t\tortp_mutex_unlock(&session->main_mutex);\n\t\t\tret = (int)msgdsize(m);\n\t\t\tm = dupmsg(m);\n\t\t\tmemcpy(&m->net_addr, destaddr, destlen);\n\t\t\tm->net_addrlen = destlen;\n\t\t\tortp_mblk_set_netsim_is_rtp_flag(m, is_rtp);\n\t\t\tusing_simulator = TRUE;\n\t\t\tortp_mutex_lock(&session->main_mutex);\n\t\t\tif (session->net_sim_ctx) putq(&session->net_sim_ctx->send_q, m);\n\t\t\telse freemsg(m);\n\t\t}\n\t\tortp_mutex_unlock(&session->main_mutex);\n\t}\n\tif (!using_simulator) {\n\t\tortp_socket_t sockfd = rtp_session_get_socket(session, is_rtp || session->rtcp_mux);\n\t\tif (sockfd != (ortp_socket_t)-1) {\n\t\t\tret = _ortp_sendto(sockfd, m, flags, destaddr, destlen);\n\t\t} else {\n\t\t\tret = -1;\n\t\t}\n\t}\n\treturn ret;\n}\n\nstatic const ortp_recv_addr_t *lookup_recv_addr(RtpSession *session, struct sockaddr *from, socklen_t fromlen) {\n\tconst ortp_recv_addr_t *result = NULL;\n\tbctbx_list_t *iterator = session->recv_addr_map;\n\twhile (iterator != NULL) {\n\t\tortp_recv_addr_map_t *item = (ortp_recv_addr_map_t *)bctbx_list_get_data(iterator);\n\t\tuint64_t curtime = bctbx_get_cur_time_ms();\n\t\tif ((curtime - item->ts) > 2000) {\n\t\t\tbctbx_list_t *to_remove = iterator;\n\t\t\titerator = bctbx_list_next(iterator);\n\t\t\tsession->recv_addr_map = bctbx_list_erase_link(session->recv_addr_map, to_remove);\n\t\t} else {\n\t\t\tif (memcmp(&item->ss, from, fromlen) == 0) result = &item->recv_addr;\n\t\t\titerator = bctbx_list_next(iterator);\n\t\t}\n\t}\n\treturn result;\n}\n\nstatic const ortp_recv_addr_t *get_recv_addr(RtpSession *session, struct sockaddr *from, socklen_t fromlen) {\n\tchar result[NI_MAXHOST] = {0};\n\tchar dest[NI_MAXHOST] = {0};\n\tstruct addrinfo *ai = NULL;\n\tint port = 0;\n\tint family = from->sa_family;\n\tint err;\n\terr = bctbx_sockaddr_to_ip_address(from, fromlen, dest, sizeof(dest), &port);\n\tif (err != 0) {\n\t\tortp_error(\"bctbx_sockaddr_to_ip_address failed\");\n\t\treturn NULL;\n\t}\n\terr = bctbx_get_local_ip_for(family, dest, port, result, sizeof(result));\n\tif (err != 0) {\n\t\tortp_error(\"bctbx_get_local_ip_for failed: dest=%s, port=%d\", dest, port);\n\t\treturn NULL;\n\t}\n\tai = bctbx_ip_address_to_addrinfo(family, SOCK_DGRAM, result, port);\n\tif (ai == NULL) {\n\t\tortp_error(\"bctbx_ip_address_to_addrinfo failed: result=%s, port=%d\", result, port);\n\t\treturn NULL;\n\t} else {\n\t\tortp_recv_addr_map_t *item = bctbx_new0(ortp_recv_addr_map_t, 1);\n\t\tmemcpy(&item->ss, from, fromlen);\n\t\titem->recv_addr.family = family;\n\t\tif (family == AF_INET) {\n\t\t\tmemcpy(&item->recv_addr.addr.ipi_addr, &((struct sockaddr_in *)ai->ai_addr)->sin_addr,\n\t\t\t       sizeof(item->recv_addr.addr.ipi_addr));\n\t\t} else if (family == AF_INET6) {\n\t\t\tmemcpy(&item->recv_addr.addr.ipi6_addr, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr,\n\t\t\t       sizeof(item->recv_addr.addr.ipi6_addr));\n\t\t}\n\t\tbctbx_freeaddrinfo(ai);\n\t\titem->ts = bctbx_get_cur_time_ms();\n\t\tsession->recv_addr_map = bctbx_list_append(session->recv_addr_map, item);\n\t\treturn &item->recv_addr;\n\t}\n}\n\nint rtp_session_recvfrom(\n    RtpSession *session, bool_t is_rtp, mblk_t *m, int flags, struct sockaddr *from, socklen_t *fromlen) {\n\tint ret = rtp_session_rtp_recv_abstract(is_rtp ? session->rtp.gs.socket : session->rtcp.gs.socket, m, flags, from,\n\t                                        fromlen);\n\tif ((ret >= 0) && (session->use_pktinfo == TRUE)) {\n\t\tif (m->recv_addr.family == AF_UNSPEC) {\n\t\t\tconst ortp_recv_addr_t *recv_addr;\n\n\t\t\tif (!session->warn_non_working_pkt_info) {\n\t\t\t\tortp_error(\"IP_PKTINFO/IP6_PKTINFO not working as expected for recevied packets. An unreliable \"\n\t\t\t\t           \"fallback method will be used.\");\n\t\t\t\tsession->warn_non_working_pkt_info = TRUE;\n\t\t\t}\n\t\t\t/* The receive address has not been filled, this typically happens on Mac OS X when receiving an IPv4 packet\n\t\t\t * on a dual stack socket. Try to guess the address because it is mandatory for ICE. */\n\t\t\trecv_addr = lookup_recv_addr(session, from, *fromlen);\n\t\t\tif (recv_addr == NULL) {\n\t\t\t\trecv_addr = get_recv_addr(session, from, *fromlen);\n\t\t\t}\n\t\t\tif (recv_addr != NULL) {\n\t\t\t\tmemcpy(&m->recv_addr, recv_addr, sizeof(ortp_recv_addr_t));\n\t\t\t} else {\n\t\t\t\tortp_error(\"Did not succeed to fill the receive address, this should not happen! [family=%d, len=%d]\",\n\t\t\t\t           from->sa_family, (int)*fromlen);\n\t\t\t}\n\t\t}\n\t\t/* Store the local port in the recv_addr of the mblk_t, the address is already filled in\n\t\t * rtp_session_rtp_recv_abstract */\n\t\tm->recv_addr.port = htons(is_rtp ? session->rtp.gs.loc_port : session->rtcp.gs.loc_port);\n\t}\n\treturn ret;\n}\n\nvoid ortp_stream_update_sent_bytes(OrtpStream *os, int nbytes) {\n\tint overhead = ortp_stream_is_ipv6(os) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;\n\tstruct timeval current;\n\tbctbx_gettimeofday(&current, NULL);\n\n\tortp_bandwidth_measurer_add_bytes(os->send_bw_estimator, nbytes + overhead, &current);\n\tortp_bandwidth_measurer_add_bytes(os->send_average_bw_estimator, nbytes + overhead, &current);\n}\n\nstatic void update_recv_bytes(OrtpStream *os, size_t nbytes, const struct timeval *recv_time) {\n\tint overhead = ortp_stream_is_ipv6(os) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;\n\tortp_bandwidth_measurer_add_bytes(os->recv_bw_estimator, nbytes + overhead, recv_time);\n\tortp_bandwidth_measurer_add_bytes(os->recv_average_bw_estimator, nbytes + overhead, recv_time);\n}\n\nstatic void\nlog_send_error(RtpSession *session, const char *type, mblk_t *m, struct sockaddr *destaddr, socklen_t destlen) {\n\tchar printable_ip_address[65] = {0};\n\tint errnum = getSocketErrorCode();\n\tconst char *errstr = getSocketError();\n\tbctbx_sockaddr_to_printable_ip_address(destaddr, destlen, printable_ip_address, sizeof(printable_ip_address));\n\tortp_error(\"RtpSession [%p] error sending [%s] packet [%p] to %s: %s [%d]\", session, type, m, printable_ip_address,\n\t           errstr, errnum);\n}\n\nstatic int\nrtp_session_rtp_sendto(RtpSession *session, mblk_t *m, struct sockaddr *destaddr, socklen_t destlen, bool_t is_aux) {\n\tint error;\n\tRtpSession *send_session = session;\n\tRtpBundle *bundle = session->bundle;\n\tOrtpStream *ostr;\n\n\tif (bundle && !session->is_primary) {\n\t\tsend_session = rtp_bundle_get_primary_session(bundle);\n\t\tif (!send_session) {\n\t\t\tortp_error(\"RtpSession [%p] error get no primary session\", session);\n\t\t\treturn -1;\n\t\t}\n\t\tdestaddr = (struct sockaddr *)&send_session->rtp.gs.rem_addr;\n\t\tdestlen = send_session->rtp.gs.rem_addrlen;\n\t}\n\tostr = &send_session->rtp.gs;\n\t/*\n\t * If the \"recv_addr\" (which is the source address to use for sending) is not specified for this packet,\n\t * default to the source address specified in the RtpSession for the rtp stream.\n\t * It may be altered by modifiers or endpoint of the RtpTransport if present.\n\t */\n\tif (m->recv_addr.family == AF_UNSPEC && ostr->used_loc_addrlen != 0)\n\t\tortp_sockaddr_to_recvaddr((const struct sockaddr *)&ostr->used_loc_addr, &m->recv_addr);\n\n\tif (rtp_session_using_transport(send_session, rtp)) {\n\t\terror = (send_session->rtp.gs.tr->t_sendto)(send_session->rtp.gs.tr, m, 0, destaddr, destlen);\n\t} else {\n\t\terror = rtp_session_sendto(send_session, TRUE, m, 0, destaddr, destlen);\n\t}\n\tif (!is_aux) {\n\t\t/*errors to auxiliary destinations are not notified*/\n\t\tif (error < 0) {\n\t\t\tif (session->on_network_error.count > 0) {\n\t\t\t\trtp_signal_table_emit3(&session->on_network_error, \"Error sending RTP packet\",\n\t\t\t\t                       ORTP_INT_TO_POINTER(getSocketErrorCode()));\n\t\t\t} else log_send_error(session, \"rtp\", m, destaddr, destlen);\n\t\t\tsession->rtp.send_errno = getSocketErrorCode();\n\t\t} else {\n\t\t\tortp_stream_update_sent_bytes(&session->rtp.gs, error);\n\t\t}\n\t}\n\treturn error;\n}\n\nint rtp_session_rtp_send(RtpSession *session, mblk_t *m) {\n\tint error = 0;\n\tstruct sockaddr *destaddr = (struct sockaddr *)&session->rtp.gs.rem_addr;\n\tsocklen_t destlen = session->rtp.gs.rem_addrlen;\n\tOList *elem = NULL;\n\n\tif (session->is_spliced) {\n\t\tfreemsg(m);\n\t\treturn 0;\n\t}\n\n\tif (session->flags & RTP_SOCKET_CONNECTED) {\n\t\tdestaddr = NULL;\n\t\tdestlen = 0;\n\t}\n\t/*first send to main destination*/\n\terror = rtp_session_rtp_sendto(session, m, destaddr, destlen, FALSE);\n\t/*then iterate over auxiliary destinations*/\n\tif (session->rtp.gs.aux_destinations) {\n\t\tortp_mutex_lock(&session->main_mutex);\n\t\tfor (elem = session->rtp.gs.aux_destinations; elem != NULL; elem = elem->next) {\n\t\t\tOrtpAddress *addr = (OrtpAddress *)elem->data;\n\t\t\trtp_session_rtp_sendto(session, m, (struct sockaddr *)&addr->addr, addr->len, TRUE);\n\t\t}\n\t\tortp_mutex_unlock(&session->main_mutex);\n\t}\n\tfreemsg(m);\n\treturn error;\n}\n\nstatic int\nrtp_session_rtcp_sendto(RtpSession *session, mblk_t *m, struct sockaddr *destaddr, socklen_t destlen, bool_t is_aux) {\n\tint error = 0;\n\tRtpSession *send_session = session;\n\tRtpBundle *bundle = session->bundle;\n\tOrtpStream *ostr;\n\n\tif (bundle && !session->is_primary) {\n\t\tsend_session = rtp_bundle_get_primary_session(bundle);\n\t\tif (send_session) {\n\t\t\tdestaddr = (struct sockaddr *)&send_session->rtp.gs.rem_addr;\n\t\t\tdestlen = send_session->rtp.gs.rem_addrlen;\n\t\t} else send_session = session;\n\t}\n\tostr = &send_session->rtcp.gs;\n\t/*\n\t * If the \"recv_addr\" (which is the source address to use for sending) is not specified for this packet,\n\t * default to the source address specified in the RtpSession for the rtcp stream.\n\t * It may be altered by modifiers or endpoint of the RtpTransport if present.\n\t */\n\tif (m->recv_addr.family == AF_UNSPEC && ostr->used_loc_addrlen != 0)\n\t\tortp_sockaddr_to_recvaddr((const struct sockaddr *)&ostr->used_loc_addr, &m->recv_addr);\n\n\t/* Even in RTCP mux, we send through the RTCP RtpTransport, which will itself take in charge to do the sending of\n\t * the packet through the RTP endpoint*/\n\tif (rtp_session_using_transport(send_session, rtcp)) {\n\t\terror = (send_session->rtcp.gs.tr->t_sendto)(send_session->rtcp.gs.tr, m, 0, destaddr, destlen);\n\t} else {\n\t\terror = _ortp_sendto(rtp_session_get_socket(send_session, send_session->rtcp_mux), m, 0, destaddr, destlen);\n\t}\n\n\tif (!is_aux) {\n\t\tif (error < 0) {\n\t\t\tif (session->on_network_error.count > 0) {\n\t\t\t\trtp_signal_table_emit3(&session->on_network_error, \"Error sending RTCP packet\",\n\t\t\t\t                       ORTP_INT_TO_POINTER(getSocketErrorCode()));\n\t\t\t} else {\n\t\t\t\tlog_send_error(session, \"rtcp\", m, destaddr, destlen);\n\t\t\t}\n\t\t} else {\n\t\t\tortp_stream_update_sent_bytes(&session->rtcp.gs, error);\n\t\t\trtp_session_update_avg_rtcp_size(session, error);\n\t\t}\n\t}\n\treturn error;\n}\n\nint rtp_session_rtcp_send(RtpSession *session, mblk_t *m) {\n\tint error = 0;\n\tortp_socket_t sockfd = session->rtcp.gs.socket;\n\tstruct sockaddr *destaddr = session->rtcp_mux ? (struct sockaddr *)&session->rtp.gs.rem_addr\n\t                                              : (struct sockaddr *)&session->rtcp.gs.rem_addr;\n\tsocklen_t destlen = session->rtcp_mux ? session->rtp.gs.rem_addrlen : session->rtcp.gs.rem_addrlen;\n\tOList *elem = NULL;\n\tbool_t using_connected_socket = (session->flags & RTCP_SOCKET_CONNECTED) != 0;\n\n\tif (session->is_spliced) {\n\t\tfreemsg(m);\n\t\treturn 0;\n\t}\n\tif (using_connected_socket) {\n\t\tdestaddr = NULL;\n\t\tdestlen = 0;\n\t}\n\tif (session->rtcp.enabled) {\n\t\tif ((sockfd != (ortp_socket_t)-1 && (destlen > 0 || using_connected_socket)) ||\n\t\t    rtp_session_using_transport(session, rtcp)) {\n\t\t\trtp_session_rtcp_sendto(session, m, destaddr, destlen, FALSE);\n\t\t}\n\t\tfor (elem = session->rtcp.gs.aux_destinations; elem != NULL; elem = elem->next) {\n\t\t\tOrtpAddress *addr = (OrtpAddress *)elem->data;\n\t\t\trtp_session_rtcp_sendto(session, m, (struct sockaddr *)&addr->addr, addr->len, TRUE);\n\t\t}\n\t} else ortp_message(\"Not sending rtcp report, rtcp disabled.\");\n\tfreemsg(m);\n\treturn error;\n}\n\n#ifdef USE_RECVMSG\nstatic int rtp_recvmsg(ortp_socket_t socket,\n                       mblk_t *msg,\n                       int flags,\n                       struct sockaddr *from,\n                       socklen_t *fromlen,\n                       struct msghdr *msghdr,\n                       int bufsz) {\n\tstruct iovec iov;\n\tint error;\n\n\tmemset(&iov, 0, sizeof(iov));\n\tiov.iov_base = msg->b_wptr;\n\tiov.iov_len = bufsz;\n\tif ((from != NULL) && (fromlen != NULL)) {\n\t\tmsghdr->msg_name = from;\n\t\tmsghdr->msg_namelen = *fromlen;\n\t}\n\tmsghdr->msg_iov = &iov;\n\tmsghdr->msg_iovlen = 1;\n\terror = recvmsg(socket, msghdr, flags);\n\tif (fromlen != NULL) *fromlen = msghdr->msg_namelen;\n\treturn error;\n}\n#endif\nint rtp_session_rtp_recv_abstract(\n    ortp_socket_t socket, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen) {\n\tint ret;\n\tint bufsz = (int)(msg->b_datap->db_lim - msg->b_datap->db_base);\n\tchar control[512] = {0};\n#ifdef _WIN32\n\tWSAMSG msghdr = {0};\n\tWSACMSGHDR *cmsghdr;\n\tWSABUF data_buf;\n\tDWORD bytes_received = 0;\n\tint error = 0;\n\tif (ortp_WSARecvMsg == NULL) {\n\t\treturn recvfrom(socket, (char *)msg->b_wptr, bufsz, flags, from, fromlen);\n\t}\n\n\tif (from != NULL && fromlen != NULL) {\n\t\tmsghdr.name = from;\n\t\tmsghdr.namelen = *fromlen;\n\t}\n\tdata_buf.buf = (char *)msg->b_wptr;\n\tdata_buf.len = bufsz;\n\tmsghdr.dwBufferCount = 1;\n\tmsghdr.lpBuffers = &data_buf;\n\tmsghdr.Control.buf = control;\n\tmsghdr.Control.len = sizeof(control);\n\tmsghdr.dwFlags = flags;\n\tret = ortp_WSARecvMsg(socket, &msghdr, &bytes_received, NULL, NULL);\n\tif (fromlen != NULL) *fromlen = msghdr.namelen;\n\tif (ret >= 0) {\n\t\tret = bytes_received;\n#else\n\tstruct msghdr msghdr = {0};\n\tmsghdr.msg_control = control;\n\tmsghdr.msg_controllen = sizeof(control);\n#ifdef USE_RECVMSG\n\tret = rtp_recvmsg(socket, msg, flags, from, fromlen, &msghdr, bufsz);\n#else\n\tret = recvfrom(socket, msg->b_wptr, bufsz, flags, from, fromlen);\n#endif\n\tif (ret >= 0) {\n\t\tstruct cmsghdr *cmsghdr;\n#endif\n\t\tfor (cmsghdr = CMSG_FIRSTHDR(&msghdr); cmsghdr != NULL; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {\n#ifdef _RECV_SO_TIMESTAMP_TYPE\n\t\t\tif (cmsghdr->cmsg_level == SOL_SOCKET && cmsghdr->cmsg_type == _RECV_SO_TIMESTAMP_TYPE) {\n\t\t\t\tmemcpy(&msg->timestamp, (struct timeval *)CMSG_DATA(cmsghdr), sizeof(struct timeval));\n\t\t\t}\n#endif\n#ifdef IP_PKTINFO\n\t\t\tif ((cmsghdr->cmsg_level == IPPROTO_IP) && (cmsghdr->cmsg_type == IP_PKTINFO)) {\n\t\t\t\tstruct in_pktinfo *pi = (struct in_pktinfo *)CMSG_DATA(cmsghdr);\n\t\t\t\tmemcpy(&msg->recv_addr.addr.ipi_addr, &pi->ipi_addr, sizeof(msg->recv_addr.addr.ipi_addr));\n\t\t\t\tmsg->recv_addr.family = AF_INET;\n\t\t\t}\n#endif\n#ifdef IPV6_PKTINFO\n\t\t\tif ((cmsghdr->cmsg_level == IPPROTO_IPV6) && (cmsghdr->cmsg_type == IPV6_PKTINFO)) {\n\t\t\t\tstruct in6_pktinfo *pi = (struct in6_pktinfo *)CMSG_DATA(cmsghdr);\n\t\t\t\tmemcpy(&msg->recv_addr.addr.ipi6_addr, &pi->ipi6_addr, sizeof(msg->recv_addr.addr.ipi6_addr));\n\t\t\t\tmsg->recv_addr.family = AF_INET6;\n\t\t\t}\n#endif\n#ifdef IP_RECVDSTADDR\n\t\t\tif ((cmsghdr->cmsg_level == IPPROTO_IP) && (cmsghdr->cmsg_type == IP_RECVDSTADDR)) {\n\t\t\t\tstruct in_addr *ia = (struct in_addr *)CMSG_DATA(cmsghdr);\n\t\t\t\tmemcpy(&msg->recv_addr.addr.ipi_addr, ia, sizeof(msg->recv_addr.addr.ipi_addr));\n\t\t\t\tmsg->recv_addr.family = AF_INET;\n\t\t\t}\n#endif\n#ifdef IPV6_RECVDSTADDR\n\t\t\tif ((cmsghdr->cmsg_level == IPPROTO_IPV6) && (cmsghdr->cmsg_type == IPV6_RECVDSTADDR)) {\n\t\t\t\tstruct in6_addr *ia = (struct in6_addr *)CMSG_DATA(cmsghdr);\n\t\t\t\tmemcpy(&msg->recv_addr.addr.ipi6_addr, ia, sizeof(msg->recv_addr.addr.ipi6_addr));\n\t\t\t\tmsg->recv_addr.family = AF_INET6;\n\t\t\t}\n#endif\n#ifdef IP_RECVTTL\n\t\t\tif ((cmsghdr->cmsg_level == IPPROTO_IP) && (cmsghdr->cmsg_type == IP_TTL)) {\n\t\t\t\tuint32_t *ptr = (uint32_t *)CMSG_DATA(cmsghdr);\n\t\t\t\tmsg->ttl_or_hl = (*ptr & 0xFF);\n\t\t\t}\n#endif\n#ifdef IPV6_RECVHOPLIMIT\n\t\t\tif ((cmsghdr->cmsg_level == IPPROTO_IPV6) && (cmsghdr->cmsg_type == IPV6_HOPLIMIT)) {\n\t\t\t\tuint32_t *ptr = (uint32_t *)CMSG_DATA(cmsghdr);\n\t\t\t\tmsg->ttl_or_hl = (*ptr & 0xFF);\n\t\t\t}\n#endif\n\t\t}\n\t\t/*store recv addr for use by modifiers*/\n\t\tif (from && fromlen) {\n\t\t\tmemcpy(&msg->net_addr, from, *fromlen);\n\t\t\tmsg->net_addrlen = *fromlen;\n\t\t}\n\t} else {\n\t}\n\treturn ret;\n}\n\nvoid rtp_session_notify_inc_rtcp(RtpSession *session, mblk_t *m, bool_t received_via_rtcp_mux) {\n\tif (session->eventqs != NULL) {\n\t\tOrtpEvent *ev = ortp_event_new(ORTP_EVENT_RTCP_PACKET_RECEIVED);\n\t\tOrtpEventData *d = ortp_event_get_data(ev);\n\t\td->packet = m;\n\t\td->info.socket_type = received_via_rtcp_mux ? OrtpRTPSocket : OrtpRTCPSocket;\n\t\trtp_session_dispatch_event(session, ev);\n\t} else freemsg(m); /* avoid memory leak */\n}\n\nstatic void compute_rtt(RtpSession *session, const struct timeval *now, uint32_t lrr, uint32_t dlrr) {\n\tuint64_t curntp = ortp_timeval_to_ntp(now);\n\tuint32_t approx_ntp = (curntp >> 16) & 0xFFFFFFFF;\n\t/*ortp_message(\"rtt approx_ntp=%u, lrr=%u, dlrr=%u\",approx_ntp,lrr,dlrr);*/\n\tif (lrr != 0 && dlrr != 0) {\n\t\t/*we cast to int32_t to check for crazy RTT time (negative)*/\n\t\tdouble rtt_frac = (int32_t)(approx_ntp - lrr - dlrr);\n\t\tif (rtt_frac >= 0) {\n\t\t\trtt_frac /= 65536.0;\n\n\t\t\tsession->rtt = (float)rtt_frac;\n\t\t\t/*ortp_message(\"rtt estimated to %f s\",session->rtt);*/\n\t\t} else ortp_warning(\"Negative RTT computation, maybe due to clock adjustments.\");\n\t}\n}\n\nstatic void compute_rtt_from_report_block(RtpSession *session, const struct timeval *now, const report_block_t *rb) {\n\tuint32_t last_sr_time = report_block_get_last_SR_time(rb);\n\tuint32_t sr_delay = report_block_get_last_SR_delay(rb);\n\tcompute_rtt(session, now, last_sr_time, sr_delay);\n\tsession->cum_loss = report_block_get_cum_packet_lost(rb);\n}\n\nstatic void compute_rtcp_xr_statistics(RtpSession *session, const mblk_t *block, const struct timeval *now) {\n\tuint64_t ntp_timestamp;\n\tOrtpRtcpXrStats *stats = &session->rtcp_xr_stats;\n\n\tswitch (rtcp_XR_get_block_type(block)) {\n\t\tcase RTCP_XR_RCVR_RTT:\n\t\t\tntp_timestamp = rtcp_XR_rcvr_rtt_get_ntp_timestamp(block);\n\t\t\tstats->last_rcvr_rtt_ts = (ntp_timestamp >> 16) & 0xffffffff;\n\t\t\tstats->last_rcvr_rtt_time.tv_sec = now->tv_sec;\n\t\t\tstats->last_rcvr_rtt_time.tv_usec = now->tv_usec;\n\t\t\tbreak;\n\t\tcase RTCP_XR_DLRR:\n\t\t\tcompute_rtt(session, now, rtcp_XR_dlrr_get_lrr(block), rtcp_XR_dlrr_get_dlrr(block));\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n}\n\nstatic void handle_rtcp_rtpfb_packet(RtpSession *session, const mblk_t *block) {\n\tswitch (rtcp_RTPFB_get_type(block)) {\n\t\tcase RTCP_RTPFB_TMMBR:\n\t\t\tif (session->rtcp.tmmbr_info.received) freemsg(session->rtcp.tmmbr_info.received);\n\t\t\tsession->rtcp.tmmbr_info.received = copymsg(block);\n\t\t\trtp_session_send_rtcp_fb_tmmbn(session, rtcp_RTPFB_get_packet_sender_ssrc(block));\n\t\t\tbreak;\n\t\tcase RTCP_RTPFB_TMMBN:\n\t\t\tif (session->rtcp.tmmbr_info.sent) {\n\t\t\t\trtcp_fb_tmmbr_fci_t *tmmbn_fci = rtcp_RTPFB_tmmbr_get_fci(block);\n\t\t\t\trtcp_fb_tmmbr_fci_t *tmmbr_fci = rtcp_RTPFB_tmmbr_get_fci(session->rtcp.tmmbr_info.sent);\n\t\t\t\tif ((ntohl(tmmbn_fci->ssrc) == rtp_session_get_send_ssrc(session)) &&\n\t\t\t\t    (tmmbn_fci->value == tmmbr_fci->value)) {\n\t\t\t\t\tfreemsg(session->rtcp.tmmbr_info.sent);\n\t\t\t\t\tsession->rtcp.tmmbr_info.sent = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n}\n\n/*\n * @brief : for SR packets, retrieves their timestamp, gets the date, and stores these information into the session\n * descriptor. The date values may be used for setting some fields of the report block of the next RTCP packet to be\n * sent.\n * @param session : the current session descriptor.\n * @param block : the block descriptor that may contain a SR RTCP message.\n * @return 0 if the packet is a real RTCP packet, -1 otherwise.\n * @note a basic parsing is done on the block structure. However, if it fails, no error is returned, and the session\n * descriptor is left as is, so it does not induce any change in the caller procedure behaviour.\n * @note the packet is freed or is taken ownership if -1 is returned\n */\nstatic int process_rtcp_packet(RtpSession *session, mblk_t *block, struct sockaddr *addr, socklen_t addrlen) {\n\trtcp_common_header_t *rtcp;\n\tRtpStream *rtpstream = &session->rtp;\n\tRtcpParserContext rtcpctx;\n\tconst mblk_t *rtcp_packet;\n\n\tint msgsize = (int)(block->b_wptr - block->b_rptr);\n\tif (msgsize < RTCP_COMMON_HEADER_SIZE) {\n\t\tortp_warning(\"Receiving a too short RTCP packet\");\n\t\tfreemsg(block);\n\t\treturn -1;\n\t}\n\n\trtcp = (rtcp_common_header_t *)block->b_rptr;\n\n\tif (rtcp->version != 2) {\n\t\t/* try to see if it is a STUN packet */\n\t\tuint16_t stunlen = *((uint16_t *)(block->b_rptr + sizeof(uint16_t)));\n\t\tstunlen = ntohs(stunlen);\n\t\tif (stunlen + 20 == block->b_wptr - block->b_rptr) {\n\t\t\t/* this looks like a stun packet */\n\t\t\trtp_session_update_remote_sock_addr(session, block, FALSE);\n\n\t\t\tif (session->eventqs != NULL) {\n\t\t\t\tOrtpEvent *ev = ortp_event_new(ORTP_EVENT_STUN_PACKET_RECEIVED);\n\t\t\t\tOrtpEventData *ed = ortp_event_get_data(ev);\n\t\t\t\ted->packet = block;\n\t\t\t\ted->source_addrlen = addrlen;\n\t\t\t\tmemcpy(&ed->source_addr, addr, addrlen);\n\t\t\t\ted->info.socket_type = OrtpRTCPSocket;\n\t\t\t\trtp_session_dispatch_event(session, ev);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t} else {\n\t\t\tortp_warning(\"RtpSession [%p] receiving rtcp packet with version number != 2, discarded\", session);\n\t\t}\n\t\tfreemsg(block);\n\t\treturn -1;\n\t}\n\n\tupdate_recv_bytes(&session->rtcp.gs, (int)(block->b_wptr - block->b_rptr), &block->timestamp);\n\n\trtcp_packet = rtcp_parser_context_init(&rtcpctx, block);\n\t/* compound rtcp packet can be composed by more than one rtcp message */\n\tdo {\n\t\tstruct timeval reception_date;\n\t\tconst report_block_t *rb;\n\n\t\t/* Getting the reception date from the main clock */\n\t\tbctbx_gettimeofday(&reception_date, NULL);\n\n\t\tif (rtcp_is_SR(rtcp_packet)) {\n\t\t\trtcp_sr_t *sr = (rtcp_sr_t *)rtcp;\n\n\t\t\t/* The session descriptor values are reset in case there is an error in the SR block parsing */\n\t\t\trtpstream->last_rcv_SR_ts = 0;\n\t\t\trtpstream->last_rcv_SR_time.tv_usec = 0;\n\t\t\trtpstream->last_rcv_SR_time.tv_sec = 0;\n\n\t\t\tif (ntohl(sr->ssrc) != session->rcv.ssrc) {\n\t\t\t\tortp_debug(\"Receiving a RTCP SR packet from an unknown ssrc\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (msgsize < RTCP_COMMON_HEADER_SIZE + RTCP_SSRC_FIELD_SIZE + RTCP_SENDER_INFO_SIZE +\n\t\t\t                  (RTCP_REPORT_BLOCK_SIZE * sr->ch.rc)) {\n\t\t\t\tortp_debug(\"Receiving a too short RTCP SR packet\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* Saving the data to fill LSR and DLSR field in next RTCP report to be transmitted */\n\t\t\t/* This value will be the LSR field of the next RTCP report (only the central 32 bits are kept, as described\n\t\t\t * in par.4 of RC3550) */\n\t\t\trtpstream->last_rcv_SR_ts =\n\t\t\t    (ntohl(sr->si.ntp_timestamp_msw) << 16) | (ntohl(sr->si.ntp_timestamp_lsw) >> 16);\n\t\t\t/* This value will help in processing the DLSR of the next RTCP report ( see report_block_init() in rtcp.cc\n\t\t\t * ) */\n\t\t\trtpstream->last_rcv_SR_time.tv_usec = reception_date.tv_usec;\n\t\t\trtpstream->last_rcv_SR_time.tv_sec = reception_date.tv_sec;\n\t\t\trb = rtcp_SR_get_report_block(rtcp_packet, 0);\n\t\t\tif (rb) compute_rtt_from_report_block(session, &reception_date, rb);\n\t\t} else if (rtcp_is_RR(rtcp_packet)) {\n\t\t\trb = rtcp_RR_get_report_block(rtcp_packet, 0);\n\t\t\tif (rb) compute_rtt_from_report_block(session, &reception_date, rb);\n\t\t} else if (rtcp_is_XR(rtcp_packet)) {\n\t\t\tcompute_rtcp_xr_statistics(session, rtcp_packet, &reception_date);\n\t\t} else if (rtcp_is_RTPFB(rtcp_packet)) {\n\t\t\thandle_rtcp_rtpfb_packet(session, rtcp_packet);\n\t\t}\n\t} while ((rtcp_packet = rtcp_parser_context_next_packet(&rtcpctx)) != NULL);\n\trtcp_parser_context_uninit(&rtcpctx);\n\trtp_session_update_remote_sock_addr(session, block, FALSE);\n\treturn 0;\n}\n\nstatic void reply_to_collaborative_rtcp_xr_packet(RtpSession *session, mblk_t *block) {\n\tif (rtcp_is_XR(block) && (rtcp_XR_get_block_type(block) == RTCP_XR_RCVR_RTT)) {\n\t\tsession->rtcp.rtcp_xr_dlrr_to_send = TRUE;\n\t}\n}\n\nstatic void rtp_process_incoming_packet(\n    RtpSession *session, mblk_t *mp, bool_t is_rtp_packet, uint32_t user_ts, bool_t received_via_rtcp_mux) {\n\tbool_t sock_connected = (is_rtp_packet && !!(session->flags & RTP_SOCKET_CONNECTED)) ||\n\t                        (!is_rtp_packet && !!(session->flags & RTCP_SOCKET_CONNECTED));\n\n\tstruct sockaddr *remaddr = NULL;\n\tsocklen_t addrlen;\n\tremaddr = (struct sockaddr *)&mp->net_addr;\n\taddrlen = mp->net_addrlen;\n\n\tif (session->spliced_session) {\n\t\t/*this will forward all traffic to the spliced session*/\n\t\trtp_session_do_splice(session, mp, is_rtp_packet);\n\t}\n\n\t/*\n\t * Symmetric RTP policy\n\t * - if a STUN packet is received AND it is the first packet received, switch destination.\n\t * - if a RTP or RTCP packet is received, switch destination.\n\t * In all other cases, we don't switch.\n\t * This logic is implemented in rtp_session_rtp_parse() and process_rtcp_packet().\n\t **/\n\n\tif (is_rtp_packet) {\n\t\tif (session->use_connect && session->symmetric_rtp && !sock_connected) {\n\t\t\t/* In the case where use_connect is false, */\n\t\t\tif (try_connect(session->rtp.gs.socket, remaddr, addrlen)) {\n\t\t\t\tsession->flags |= RTP_SOCKET_CONNECTED;\n\t\t\t}\n\t\t}\n\t\t/* then parse the message and put on jitter buffer queue */\n\t\tupdate_recv_bytes(&session->rtp.gs, (size_t)(mp->b_wptr - mp->b_rptr), &mp->timestamp);\n\t\trtp_session_rtp_parse(session, mp, user_ts, remaddr, addrlen);\n\t\t/*for bandwidth measurements:*/\n\t} else {\n\t\tif (session->use_connect && session->symmetric_rtp && !sock_connected) {\n\t\t\tif (try_connect(session->rtcp.gs.socket, remaddr, addrlen)) {\n\t\t\t\tsession->flags |= RTCP_SOCKET_CONNECTED;\n\t\t\t}\n\t\t}\n\t\tif (process_rtcp_packet(session, mp, remaddr, addrlen) == 0) {\n\t\t\t/* a copy is needed since rtp_session_notify_inc_rtcp will free the mp,\n\t\t\tand we don't want to send RTCP XR packet before notifying the application\n\t\t\tthat a message has been received*/\n\t\t\tmblk_t *copy = copymsg(mp);\n\t\t\tsession->stats.recv_rtcp_packets++;\n\t\t\t/* post an event to notify the application */\n\t\t\trtp_session_notify_inc_rtcp(session, mp, received_via_rtcp_mux);\n\t\t\t/* reply to collaborative RTCP XR packets if needed. */\n\t\t\tif (session->rtcp.xr_conf.enabled == TRUE) {\n\t\t\t\treply_to_collaborative_rtcp_xr_packet(session, copy);\n\t\t\t}\n\t\t\tfreemsg(copy);\n\t\t}\n\t}\n}\n\nvoid rtp_session_process_incoming(\n    RtpSession *session, mblk_t *mp, bool_t is_rtp_packet, uint32_t ts, bool_t received_via_rtcp_mux) {\n\tif (session->net_sim_ctx) {\n\t\tortp_mutex_lock(&session->main_mutex);\n\t\tif (session->net_sim_ctx && session->net_sim_ctx->params.mode == OrtpNetworkSimulatorInbound) {\n\t\t\t/*drain possible packets queued in the network simulator*/\n\t\t\tmp = rtp_session_network_simulate(session, mp, &is_rtp_packet);\n\t\t}\n\t\tortp_mutex_unlock(&session->main_mutex);\n\t}\n\tif (mp != NULL) {\n\t\t/*BUG here: received_via_rtcp_mux is not preserved by network simulator*/\n\t\trtp_process_incoming_packet(session, mp, is_rtp_packet, ts, received_via_rtcp_mux);\n\t}\n}\n\nstatic mblk_t *rtp_session_alloc_recv_block(RtpSession *session) {\n\tmblk_t *m = NULL;\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\tortp_mutex_lock(&session->rtp.winrq_lock);\n#endif\n\tif ((m = session->recv_block_cache) != NULL) {\n\t\tsession->recv_block_cache = NULL;\n\t}\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\tortp_mutex_unlock(&session->rtp.winrq_lock);\n#endif\n\tif (!m) {\n\t\tm = allocb(session->recv_buf_size, 0);\n\t}\n\treturn m;\n}\n\nstatic void rtp_session_recycle_recv_block(RtpSession *session, mblk_t *m) {\n\tif (dblk_ref_value(m->b_datap) > 1) {\n\t\t/* The mblk_t may have been duplicated by the RtpTransport/RtpModifier. We shall consider the underlying buffer\n\t\t * cannot be used anymore */\n\t\tortp_warning(\"The RtpTransport has kept a ref on a mblk_t's underlying buffer. This prevents recycling.\");\n\t\tfreemsg(m);\n\t\treturn;\n\t}\n\t/* Reset read, write pointers to their original place. Indeed the RtpTransport/RtpModifier may have play\n\t * with them before discarding the mblk_t. */\n\tm->b_wptr = m->b_rptr = m->b_datap->db_base;\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\tortp_mutex_lock(&session->rtp.winrq_lock);\n#endif\n\tif (session->recv_block_cache == NULL) {\n\t\tsession->recv_block_cache = m;\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\t\tortp_mutex_unlock(&session->rtp.winrq_lock);\n\t} else {\n\t\tortp_mutex_unlock(&session->rtp.winrq_lock);\n#else\n\t} else {\n\t\tortp_error(\"Should not happen with mono-thread: Only one cache should be used.\");\n#endif\n\t\tfreeb(m);\n\t}\n}\n\nvoid *rtp_session_recvfrom_async(void *obj) {\n\tRtpSession *session = (RtpSession *)obj;\n\tint error;\n\tstruct sockaddr_storage remaddr;\n\tsocklen_t addrlen = sizeof(remaddr);\n\tmblk_t *mp;\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\tWSAPOLLFD fdarray = {0};\n\tfdarray.fd = session->rtp.gs.socket;\n\tfdarray.events = POLLRDNORM;\n\n\tif (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST)) {\n\t\tortp_warning(\"Error rtp recv async thread for windows: SetThreadPriority() failed [%d]\\n\", (int)GetLastError());\n\t}\n\n\twhile (session->rtp.is_win_thread_running) {\n\t\tint ret = WSAPoll(&fdarray, 1, 10);\n\t\taddrlen = sizeof(struct sockaddr_storage);\n\t\tif (ret == SOCKET_ERROR) {\n\t\t\tortp_warning(\"Error rtp recv async thread for windows: error while polling [%i]\", WSAGetLastError());\n\t\t} else if (ret >= 0) {\n#endif\n\t\t\tbool_t sock_connected = !!(session->flags & RTP_SOCKET_CONNECTED);\n\n\t\t\tmp = rtp_session_alloc_recv_block(session);\n\n\t\t\tif (sock_connected) {\n\t\t\t\terror = rtp_session_recvfrom(session, TRUE, mp, 0, NULL, NULL);\n\t\t\t} else if (rtp_session_using_transport(session, rtp)) {\n\t\t\t\terror =\n\t\t\t\t    (session->rtp.gs.tr->t_recvfrom)(session->rtp.gs.tr, mp, 0, (struct sockaddr *)&remaddr, &addrlen);\n\t\t\t} else {\n\t\t\t\terror = rtp_session_recvfrom(session, TRUE, mp, 0, (struct sockaddr *)&remaddr, &addrlen);\n\t\t\t}\n\n\t\t\tif (error > 0) {\n\t\t\t\tif (mp->timestamp.tv_sec == 0) {\n\t\t\t\t\tstatic int warn_once =\n\t\t\t\t\t    1; /*VERY BAD to use a static but there is no context in this function to hold this variable*/\n\t\t\t\t\tif (warn_once) {\n\t\t\t\t\t\tortp_warning(\n\t\t\t\t\t\t    \"The transport layer doesn't implement SO_TIMESTAMP, will use gettimeofday() instead.\");\n\t\t\t\t\t\twarn_once = 0;\n\t\t\t\t\t}\n\t\t\t\t\tbctbx_gettimeofday(&mp->timestamp, NULL);\n\t\t\t\t}\n\n\t\t\t\tmp->b_wptr += error;\n\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\t\t\t\tortp_mutex_lock(&session->rtp.winrq_lock);\n#endif\n\t\t\t\tputq(&session->rtp.winrq, mp);\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\t\t\t\tortp_mutex_unlock(&session->rtp.winrq_lock);\n#endif\n\t\t\t} else {\n\t\t\t\tint errnum;\n\t\t\t\tif (error == -1 && !is_would_block_error((errnum = getSocketErrorCode()))) {\n\t\t\t\t\tif (session->on_network_error.count > 0) {\n\t\t\t\t\t\trtp_signal_table_emit3(&session->on_network_error, \"Error receiving RTP packet\",\n\t\t\t\t\t\t                       ORTP_INT_TO_POINTER(getSocketErrorCode()));\n#ifdef _WIN32\n\t\t\t\t\t} else if (errnum != WSAECONNRESET)\n\t\t\t\t\t\tortp_warning(\"Error receiving RTP packet err num [%i], error [%i]: %s\", errnum, error,\n\t\t\t\t\t\t             getSocketError()); // Windows spam WSAECONNRESET and is not useful\n#else\n\t\t\t} else\n\t\t\t\tortp_warning(\"Error receiving RTP packet err num [%i], error [%i]: %s\", errnum, error,\n\t\t\t\t             getSocketError());\n#endif\n#if TARGET_OS_IPHONE\n\t\t\t\t\t/*hack for iOS and non-working socket because of background mode*/\n\t\t\t\t\tif (errnum == ENOTCONN) {\n\t\t\t\t\t\t/*re-create new sockets */\n\t\t\t\t\t\trtp_session_set_local_addr(session, session->rtp.gs.sockfamily == AF_INET ? \"0.0.0.0\" : \"::0\",\n\t\t\t\t\t\t                           session->rtp.gs.loc_port, session->rtcp.gs.loc_port);\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t\trtp_session_recycle_recv_block(session, mp);\n\t\t\t}\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\t\t}\n\t}\n#endif\n\n\treturn NULL;\n}\n\nint rtp_session_rtp_recv(RtpSession *session, uint32_t user_ts) {\n\tmblk_t *mp;\n\tbool_t more_data = TRUE;\n\tRtpBundle *bundle = session->bundle;\n\n\tif ((session->rtp.gs.socket == (ortp_socket_t)-1) && !rtp_session_using_transport(session, rtp))\n\t\treturn -1; /*session has no sockets for the moment*/\n\n\tdo {\n\t\tbool_t packet_is_rtp = TRUE;\n\t\tif (!bundle || (bundle && session->is_primary)) {\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\t\t\tortp_mutex_lock(&session->rtp.winthread_lock);\n\t\t\tif (!session->rtp.is_win_thread_running) {\n\t\t\t\tint errnum;\n\n\t\t\t\tsession->rtp.is_win_thread_running = TRUE;\n\t\t\t\tif ((errnum = ortp_thread_create(&session->rtp.win_t, NULL, rtp_session_recvfrom_async,\n\t\t\t\t                                 (void *)session)) != 0) {\n\t\t\t\t\tortp_warning(\"Error creating rtp recv async thread for windows: error [%i]\", errnum);\n\t\t\t\t\tsession->rtp.is_win_thread_running = FALSE;\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tortp_mutex_unlock(&session->rtp.winthread_lock);\n#else\n\t\t\trtp_session_recvfrom_async((void *)session);\n#endif\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\t\t\tortp_mutex_lock(&session->rtp.winrq_lock);\n#endif\n\t\t\tmp = getq(&session->rtp.winrq);\n#if defined(_WIN32) || defined(_WIN32_WCE)\n\t\t\tortp_mutex_unlock(&session->rtp.winrq_lock);\n#endif\n\t\t\tif (mp) {\n\t\t\t\t/* we got a packet from a primary transport. If bundle mode is on, it may be dispatched to another\n\t\t\t\t * session, or may be re-qualified as RTCP (rtcp-mux) */\n\t\t\t\tif (session->rtcp_mux || bundle) {\n\t\t\t\t\tif (rtp_get_version(mp) == 2) {\n\t\t\t\t\t\tint pt = rtp_get_payload_type(mp);\n\t\t\t\t\t\tif (pt >= 64 && pt <= 95) {\n\t\t\t\t\t\t\t/*this is assumed to be an RTCP packet*/\n\t\t\t\t\t\t\tpacket_is_rtp = FALSE;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// RtpBundle's dispatch method will return a packet if the destination is the primary.\n\t\t\t\tif (bundle) mp = rtp_bundle_dispatch(bundle, packet_is_rtp, mp);\n\t\t\t} else {\n\t\t\t\tmore_data = FALSE;\n\t\t\t}\n\t\t} else {\n\t\t\t/* case where we are part of a bundle as a secondary session */\n\t\t\tortp_mutex_lock(&session->rtp.gs.bundleq_lock);\n\t\t\tmp = getq(&session->rtp.gs.bundleq);\n\t\t\tortp_mutex_unlock(&session->rtp.gs.bundleq_lock);\n\t\t\tif (!mp) more_data = FALSE;\n\t\t}\n\t\trtp_session_process_incoming(session, mp, packet_is_rtp, user_ts, !packet_is_rtp);\n\t} while (more_data);\n\treturn -1;\n}\n\nint rtp_session_rtcp_recv(RtpSession *session) {\n\tint error;\n\tstruct sockaddr_storage remaddr;\n\tsocklen_t addrlen = sizeof(remaddr);\n\tmblk_t *mp;\n\n\t/* In bundle mode, rtcp-mux is used. There is nothing that needs to be read on the rtcp socket.\n\t * The RTCP packets are received on the RTP socket, and dispatched to the rtcp bundleq of their corresponding\n\t * session, or treated directly by the primary session if they belong to this primary session.\n\t */\n\twhile (1) {\n\t\tbool_t sock_connected = !!(session->flags & RTCP_SOCKET_CONNECTED);\n\t\tmp = NULL;\n\t\tif (!session->bundle) {\n\n\t\t\tif (session->rtcp.gs.socket == (ortp_socket_t)-1 && !rtp_session_using_transport(session, rtcp))\n\t\t\t\treturn -1; /*session has no RTCP sockets for the moment*/\n\n\t\t\tmp = rtp_session_alloc_recv_block(session);\n\n\t\t\tif (sock_connected) {\n\t\t\t\terror = rtp_session_recvfrom(session, FALSE, mp, 0, NULL, NULL);\n\t\t\t} else {\n\t\t\t\taddrlen = sizeof(remaddr);\n\n\t\t\t\tif (rtp_session_using_transport(session, rtcp)) {\n\t\t\t\t\terror = (session->rtcp.gs.tr->t_recvfrom)(session->rtcp.gs.tr, mp, 0, (struct sockaddr *)&remaddr,\n\t\t\t\t\t                                          &addrlen);\n\t\t\t\t} else {\n\t\t\t\t\terror = rtp_session_recvfrom(session, FALSE, mp, 0, (struct sockaddr *)&remaddr, &addrlen);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (error > 0) {\n\t\t\t\tmp->b_wptr += error;\n\t\t\t\tif (mp->timestamp.tv_sec == 0) bctbx_gettimeofday(&mp->timestamp, NULL);\n\t\t\t} else {\n\t\t\t\tint errnum;\n\t\t\t\tif (error == -1 && !is_would_block_error((errnum = getSocketErrorCode()))) {\n\t\t\t\t\tif (session->on_network_error.count > 0) {\n\t\t\t\t\t\trtp_signal_table_emit3(&session->on_network_error, \"Error receiving RTCP packet\",\n\t\t\t\t\t\t                       ORTP_INT_TO_POINTER(getSocketErrorCode()));\n\t\t\t\t\t} else\n\t\t\t\t\t\tortp_warning(\"Error receiving RTCP packet: %s, err num  [%i],error [%i]\", getSocketError(),\n\t\t\t\t\t\t             errnum, error);\n#if TARGET_OS_IPHONE\n\t\t\t\t\t/*hack for iOS and non-working socket because of background mode*/\n\t\t\t\t\tif (errnum == ENOTCONN) {\n\t\t\t\t\t\t/*re-create new sockets */\n\t\t\t\t\t\trtp_session_set_local_addr(session, session->rtcp.gs.sockfamily == AF_INET ? \"0.0.0.0\" : \"::0\",\n\t\t\t\t\t\t                           session->rtcp.gs.loc_port, session->rtcp.gs.loc_port);\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tsession->rtp.recv_errno = errnum;\n\t\t\t\t} else {\n\t\t\t\t\t/*EWOULDBLOCK errors or transports returning 0 are ignored.*/\n\t\t\t\t\trtp_session_process_incoming(session, NULL, FALSE, session->rtp.rcv_last_app_ts, FALSE);\n\t\t\t\t}\n\n\t\t\t\trtp_session_recycle_recv_block(session, mp);\n\t\t\t\tmp = NULL;\n\t\t\t}\n\t\t} else if (!session->is_primary) {\n\t\t\t/* case where we are part of a bundle as a secondary session */\n\t\t\tortp_mutex_lock(&session->rtcp.gs.bundleq_lock);\n\t\t\tmp = getq(&session->rtcp.gs.bundleq);\n\t\t\tortp_mutex_unlock(&session->rtcp.gs.bundleq_lock);\n\t\t}\n\t\tif (mp) {\n\t\t\trtp_session_process_incoming(session, mp, FALSE, session->rtp.rcv_last_app_ts, session->bundle != NULL);\n\t\t} else break;\n\t}\n\treturn 0;\n}\n\nint rtp_session_update_remote_sock_addr(RtpSession *session, mblk_t *mp, bool_t is_rtp) {\n\tstruct sockaddr_storage *rem_addr = NULL;\n\tsocklen_t *rem_addrlen;\n\tconst char *socket_type;\n\tbool_t sock_connected;\n\tbool_t *remote_address_adaptation;\n\n\tif (!rtp_session_get_symmetric_rtp(session)) return -1; /*nothing to try if not rtp symetric*/\n\n\tif (session->bundle && !session->is_primary) {\n\t\t/* A session part of a bundle not owning the transport layer shall not update remote address.*/\n\t\treturn -1;\n\t}\n\tif (is_rtp) {\n\t\trem_addr = &session->rtp.gs.rem_addr;\n\t\trem_addrlen = &session->rtp.gs.rem_addrlen;\n\t\tremote_address_adaptation = &session->rtp.gs.remote_address_adaptation;\n\t\tsocket_type = \"rtp\";\n\t\tsock_connected = session->flags & RTP_SOCKET_CONNECTED;\n\t} else {\n\t\trem_addr = &session->rtcp.gs.rem_addr;\n\t\trem_addrlen = &session->rtcp.gs.rem_addrlen;\n\t\tremote_address_adaptation = &session->rtcp.gs.remote_address_adaptation;\n\t\tsocket_type = \"rtcp\";\n\t\tsock_connected = session->flags & RTCP_SOCKET_CONNECTED;\n\t}\n\n\tif (*remote_address_adaptation == TRUE && rem_addr && !sock_connected &&\n\t    !bctbx_is_multicast_addr((const struct sockaddr *)rem_addr) &&\n\t    memcmp(rem_addr, &mp->net_addr, mp->net_addrlen) != 0) {\n\t\tchar current_ip_address[64] = {0};\n\t\tchar new_ip_address[64] = {0};\n\n\t\tbctbx_sockaddr_to_printable_ip_address((struct sockaddr *)rem_addr, *rem_addrlen, current_ip_address,\n\t\t                                       sizeof(current_ip_address));\n\t\tbctbx_sockaddr_to_printable_ip_address((struct sockaddr *)&mp->net_addr, mp->net_addrlen, new_ip_address,\n\t\t                                       sizeof(new_ip_address));\n\t\tortp_message(\"Switching %s destination from %s to %s for session [%p]\", socket_type, current_ip_address,\n\t\t             new_ip_address, session);\n\n\t\tmemcpy(rem_addr, &mp->net_addr, mp->net_addrlen);\n\t\t*rem_addrlen = mp->net_addrlen;\n\n\t\t*remote_address_adaptation = FALSE;\n#ifdef WIN32\n\t\tif (is_rtp) {\n\t\t\t/*re-apply dscp settings for the new destination (windows specific).*/\n\t\t\trtp_session_set_dscp(session, -1);\n\t\t}\n#endif\n\t\treturn 0;\n\t}\n\treturn -1;\n}\n\nvoid rtp_session_use_local_addr(RtpSession *session, const char *rtp_local_addr, const char *rtcp_local_addr) {\n\tstruct addrinfo *session_addr_info;\n\tif (rtp_local_addr[0] != '\\0') { // rtp_local_addr should not be NULL\n\t\tsession_addr_info = bctbx_ip_address_to_addrinfo(session->rtp.gs.sockfamily, SOCK_DGRAM, rtp_local_addr, 0);\n\t\tmemcpy(&session->rtp.gs.used_loc_addr, session_addr_info->ai_addr, session_addr_info->ai_addrlen);\n\t\tsession->rtp.gs.used_loc_addrlen = (socklen_t)session_addr_info->ai_addrlen;\n\t\tbctbx_freeaddrinfo(session_addr_info);\n\t} else {\n\t\tsession->rtp.gs.used_loc_addrlen = 0;\n\t\tmemset(&session->rtp.gs.used_loc_addr, 0, sizeof(session->rtp.gs.used_loc_addr)); // To not let tracks on memory\n\t}\n\tif (rtcp_local_addr[0] != '\\0') { // rtcp_local_addr should not be NULL\n\t\tsession_addr_info = bctbx_ip_address_to_addrinfo(session->rtcp.gs.sockfamily, SOCK_DGRAM, rtcp_local_addr, 0);\n\t\tmemcpy(&session->rtcp.gs.used_loc_addr, session_addr_info->ai_addr, session_addr_info->ai_addrlen);\n\t\tsession->rtcp.gs.used_loc_addrlen = (socklen_t)session_addr_info->ai_addrlen;\n\t\tbctbx_freeaddrinfo(session_addr_info);\n\t} else {\n\t\tsession->rtcp.gs.used_loc_addrlen = 0;\n\t\tmemset(&session->rtcp.gs.used_loc_addr, 0,\n\t\t       sizeof(session->rtcp.gs.used_loc_addr)); // To not let tracks on memory\n\t}\n\tortp_message(\"RtpSession set sources to [%s] and [%s]\", rtp_local_addr, rtcp_local_addr);\n}\n"
  },
  {
    "path": "src/rtpsession_priv.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef rtpsession_priv_h\n#define rtpsession_priv_h\n\n#include \"ortp/rtpsession.h\"\n\n#define IP_UDP_OVERHEAD (20 + 8)\n#define IP6_UDP_OVERHEAD (40 + 8)\n\n#define RTCP_XR_GMIN 16 /* Recommended value of Gmin from RFC3611, section 4.7.6 */\n\ntypedef enum {\n\tRTP_SESSION_RECV_SYNC = 1, /* the rtp session is synchronising in the incoming stream */\n\tRTP_SESSION_FIRST_PACKET_DELIVERED = 1 << 1,\n\tRTP_SESSION_SCHEDULED = 1 << 2,         /* the scheduler controls this session*/\n\tRTP_SESSION_BLOCKING_MODE = 1 << 3,     /* in blocking mode */\n\tRTP_SESSION_RECV_NOT_STARTED = 1 << 4,  /* the application has not started to try to recv */\n\tRTP_SESSION_SEND_NOT_STARTED = 1 << 5,  /* the application has not started to send something */\n\tRTP_SESSION_IN_SCHEDULER = 1 << 6,      /* the rtp session is in the scheduler list */\n\tRTP_SESSION_USING_EXT_SOCKETS = 1 << 7, /* the session is using externaly supplied sockets */\n\tRTP_SOCKET_CONNECTED = 1 << 8,\n\tRTCP_SOCKET_CONNECTED = 1 << 9,\n\tRTP_SESSION_USING_TRANSPORT = 1 << 10,\n\tRTCP_OVERRIDE_LOST_PACKETS = 1 << 11,\n\tRTCP_OVERRIDE_JITTER = 1 << 12,\n\tRTCP_OVERRIDE_DELAY = 1 << 13,\n\tRTP_SESSION_RECV_SEQ_INIT = 1 << 14,\n\tRTP_SESSION_FLUSH = 1 << 15,\n\tRTP_SESSION_SOCKET_REFRESH_REQUESTED = 1 << 16\n} RtpSessionFlags;\n\n#define rtp_session_using_transport(s, stream) (((s)->flags & RTP_SESSION_USING_TRANSPORT) && (s->stream.gs.tr != 0))\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint rtp_session_rtp_recv_abstract(\n    ortp_socket_t socket, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen);\n\nvoid rtp_session_update_payload_type(RtpSession *session, int pt);\nint rtp_putq(queue_t *q, mblk_t *mp);\nmblk_t *rtp_peekq(queue_t *q, uint32_t ts, int *rejected);\nint rtp_session_rtp_recv(RtpSession *session, uint32_t ts);\nint rtp_session_rtcp_recv(RtpSession *session);\nint rtp_session_rtp_send(RtpSession *session, mblk_t *m);\n\n#define rtp_session_rtcp_send rtp_session_rtcp_sendm_raw\n\nvoid rtp_session_rtp_parse(\n    RtpSession *session, mblk_t *mp, uint32_t local_str_ts, struct sockaddr *addr, socklen_t addrlen);\n\nvoid rtp_session_run_rtcp_send_scheduler(RtpSession *session);\nvoid rtp_session_update_avg_rtcp_size(RtpSession *session, int bytes);\n\nmblk_t *rtp_session_network_simulate(RtpSession *session, mblk_t *input, bool_t *is_rtp_packet);\nvoid ortp_network_simulator_stop_thread(OrtpNetworkSimulatorCtx *sim);\nvoid ortp_network_simulator_destroy(OrtpNetworkSimulatorCtx *sim);\n\nvoid rtcp_common_header_init(rtcp_common_header_t *ch, RtpSession *s, int type, int rc, size_t bytes_len);\n\nmblk_t *make_xr_rcvr_rtt(RtpSession *session);\nmblk_t *make_xr_dlrr(RtpSession *session);\nmblk_t *make_xr_stat_summary(RtpSession *session);\nmblk_t *make_xr_voip_metrics(RtpSession *session);\n\nbool_t rtcp_is_RTPFB_internal(const mblk_t *m);\nbool_t rtcp_is_PSFB_internal(const mblk_t *m);\nbool_t rtp_session_has_fb_packets_to_send(RtpSession *session);\nvoid rtp_session_send_regular_rtcp_packet_and_reschedule(RtpSession *session, uint64_t tc);\nvoid rtp_session_send_fb_rtcp_packet_and_reschedule(RtpSession *session);\n\nvoid ortp_stream_clear_aux_addresses(OrtpStream *os);\n/*\n * no more public, use modifier instead\n * */\nvoid rtp_session_set_transports(RtpSession *session, RtpTransport *rtptr, RtpTransport *rtcptr);\n\nbool_t rtp_profile_is_telephone_event(const RtpProfile *prof, int pt);\n\nortp_socket_t rtp_session_get_socket(RtpSession *session, bool_t is_rtp);\n\nvoid rtp_session_do_splice(RtpSession *session, mblk_t *packet, bool_t is_rtp);\n\n/*\n * Update remote addr in the following case:\n * rtp symetric == TRUE && socket not connected && remote addr has changed && ((rtp/rtcp packet && not only at start) or\n * (no rtp/rtcp packets received))\n * @param[in] session  on which to perform change\n * @param[in] mp packet where remote addr is retreived\n * @param[in] is_rtp true if rtp\n * @param[in] only_at_start only perform changes if no valid packets received yet\n * @return 0 if chaged was performed\n *\n */\n\nint rtp_session_update_remote_sock_addr(RtpSession *session, mblk_t *mp, bool_t is_rtp);\n\nvoid rtp_session_process_incoming(\n    RtpSession *session, mblk_t *mp, bool_t is_rtp_packet, uint32_t ts, bool_t received_via_rtcp_mux);\nvoid ortp_stream_update_sent_bytes(OrtpStream *os, int nbytes);\n\nvoid _rtp_session_apply_socket_sizes(RtpSession *session);\n\nvoid jb_parameters_init(JBParameters *jbp);\nvoid rtp_session_init_jitter_buffer(RtpSession *session);\n\nsize_t rtp_session_calculate_packet_header_size(int cc, const char *mid);\n\nvoid _rtp_session_release_sockets(RtpSession *session, bool_t release_transports);\nvoid rtp_session_set_bundle(RtpSession *session, RtpBundle *bundle);\n\nvoid rtp_bundle_session_mode_updated(RtpBundle *bundle, RtpSession *session, RtpSessionMode previous_mode);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/rtpsignaltable.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n\n#include \"utils.h\"\n#include <ortp/rtpsession.h>\n\nvoid rtp_signal_table_init(RtpSignalTable *table, RtpSession *session, const char *signal_name) {\n\tmemset(table, 0, sizeof(RtpSignalTable));\n\ttable->session = session;\n\ttable->signal_name = signal_name;\n\tsession->signal_tables = o_list_append(session->signal_tables, (void *)table);\n\tbctbx_mutex_init(&table->callback_mutex, NULL);\n}\n\nvoid rtp_signal_table_uninit(RtpSignalTable *table) {\n\tbctbx_mutex_destroy(&table->callback_mutex);\n}\n\nint rtp_signal_table_add(RtpSignalTable *table, RtpCallback cb, void *user_data) {\n\treturn rtp_signal_table_add_from_source_session(table, cb, user_data, NULL);\n}\n\nint rtp_signal_table_add_from_source_session(RtpSignalTable *table,\n                                             RtpCallback cb,\n                                             void *user_data,\n                                             const struct _RtpSession *source) {\n\tbctbx_mutex_lock(&table->callback_mutex);\n\n\tfor (int i = 0; i < RTP_CALLBACK_TABLE_MAX_ENTRIES; i++) {\n\t\tif (table->callback[i].cb == NULL) {\n\t\t\ttable->callback[i].cb = cb;\n\t\t\ttable->callback[i].user_data = user_data;\n\t\t\ttable->callback[i].source = source;\n\t\t\ttable->count++;\n\n\t\t\tbctbx_mutex_unlock(&table->callback_mutex);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tbctbx_mutex_unlock(&table->callback_mutex);\n\treturn -1;\n}\n\nvoid rtp_signal_table_emit(RtpSignalTable *table) {\n\trtp_signal_table_emit3(table, NULL, NULL);\n}\n\nvoid rtp_signal_table_emit2(RtpSignalTable *table, void *arg) {\n\trtp_signal_table_emit3(table, arg, NULL);\n}\n\nvoid rtp_signal_table_emit3(RtpSignalTable *table, void *arg1, void *arg2) {\n\tfor (int i = 0, c = 0; c < table->count; i++) {\n\t\tbctbx_mutex_lock(&table->callback_mutex);\n\t\tif (table->callback[i].cb != NULL) {\n\t\t\tc++; /*I like it*/\n\n\t\t\t// Place the user_data at the right place depending on which emits has been called\n\t\t\tif (arg1 == NULL && arg2 == NULL) {\n\t\t\t\ttable->callback[i].cb(table->session, table->callback[i].user_data, NULL, NULL);\n\t\t\t} else if (arg2 == NULL) {\n\t\t\t\ttable->callback[i].cb(table->session, arg1, table->callback[i].user_data, NULL);\n\t\t\t} else {\n\t\t\t\ttable->callback[i].cb(table->session, arg1, arg2, table->callback[i].user_data);\n\t\t\t}\n\t\t}\n\t\tbctbx_mutex_unlock(&table->callback_mutex);\n\t}\n}\n\nint rtp_signal_table_remove_by_callback(RtpSignalTable *table, RtpCallback cb) {\n\treturn rtp_signal_table_remove_by_callback_and_user_data(table, cb, NULL);\n}\n\nint rtp_signal_table_remove_by_callback_and_user_data(RtpSignalTable *table, RtpCallback cb, void *user_data) {\n\tbctbx_mutex_lock(&table->callback_mutex);\n\n\tfor (int i = 0; i < RTP_CALLBACK_TABLE_MAX_ENTRIES; i++) {\n\t\tif (table->callback[i].cb == cb) {\n\t\t\tif (user_data == NULL || user_data == table->callback[i].user_data) {\n\t\t\t\ttable->callback[i].cb = NULL;\n\t\t\t\ttable->callback[i].user_data = NULL;\n\t\t\t\ttable->callback[i].source = NULL;\n\t\t\t\ttable->count--;\n\n\t\t\t\tbctbx_mutex_unlock(&table->callback_mutex);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tbctbx_mutex_unlock(&table->callback_mutex);\n\treturn -1;\n}\n\nint rtp_signal_table_remove_by_source_session(RtpSignalTable *table, const RtpSession *session) {\n\tbctbx_mutex_lock(&table->callback_mutex);\n\n\tfor (int i = 0; i < RTP_CALLBACK_TABLE_MAX_ENTRIES; i++) {\n\t\tif (table->callback[i].source == session) {\n\t\t\ttable->callback[i].cb = NULL;\n\t\t\ttable->callback[i].user_data = NULL;\n\t\t\ttable->callback[i].source = NULL;\n\t\t\ttable->count--;\n\n\t\t\tbctbx_mutex_unlock(&table->callback_mutex);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tbctbx_mutex_unlock(&table->callback_mutex);\n\treturn -1;\n}\n"
  },
  {
    "path": "src/rtptimer.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"rtptimer.h\"\n#include \"ortp/ortp.h\"\n\nvoid rtp_timer_set_interval(RtpTimer *timer, struct timeval *interval) {\n\tif (timer->state == RTP_TIMER_RUNNING) {\n\t\tortp_warning(\"Cannot change timer interval while it is running.\\n\");\n\t\treturn;\n\t}\n\ttimer->interval.tv_sec = interval->tv_sec;\n\ttimer->interval.tv_usec = interval->tv_usec;\n}\n"
  },
  {
    "path": "src/rtptimer.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef RTPTIMER_H\n#define RTPTIMER_H\n\n#if !defined(_WIN32) && !defined(_WIN32_WCE)\n#include <sys/time.h>\n#else\n#include \"winsock2.h\"\n#include <time.h>\n#endif\n\n#include <ortp/port.h>\n\ntypedef void (*RtpTimerFunc)(void);\n\nstruct _RtpTimer {\n\tint state;\n#define RTP_TIMER_RUNNING 1\n#define RTP_TIMER_STOPPED 0\n\tRtpTimerFunc timer_init;\n\tRtpTimerFunc timer_do;\n\tRtpTimerFunc timer_uninit;\n\tstruct timeval interval;\n};\n\ntypedef struct _RtpTimer RtpTimer;\n\nORTP_PUBLIC void rtp_timer_set_interval(RtpTimer *timer, struct timeval *interval);\n\nORTP_VAR_PUBLIC RtpTimer posix_timer;\n\n#endif\n"
  },
  {
    "path": "src/scheduler.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include \"rtpsession_priv.h\"\n#include \"scheduler.h\"\n#include \"utils.h\"\n#include <ortp/ortp.h>\n\n// To avoid warning during compile\nextern void rtp_session_process(RtpSession *session, uint32_t time, RtpScheduler *sched);\n\nvoid rtp_scheduler_init(RtpScheduler *sched) {\n\tsched->list = 0;\n\tsched->time_ = 0;\n\t/* default to the posix timer */\n#if !defined(ORTP_WINDOWS_UNIVERSAL)\n\trtp_scheduler_set_timer(sched, &posix_timer);\n#endif\n\tortp_mutex_init(&sched->lock, NULL);\n\tortp_cond_init(&sched->unblock_select_cond, NULL);\n\tsched->max_sessions = sizeof(SessionSet) * 8;\n\tsession_set_init(&sched->all_sessions);\n\tsched->all_max = 0;\n\tsession_set_init(&sched->r_sessions);\n\tsched->r_max = 0;\n\tsession_set_init(&sched->w_sessions);\n\tsched->w_max = 0;\n\tsession_set_init(&sched->e_sessions);\n\tsched->e_max = 0;\n}\n\nRtpScheduler *rtp_scheduler_new(void) {\n\tRtpScheduler *sched = (RtpScheduler *)ortp_malloc(sizeof(RtpScheduler));\n\tmemset(sched, 0, sizeof(RtpScheduler));\n\trtp_scheduler_init(sched);\n\treturn sched;\n}\n\nvoid rtp_scheduler_set_timer(RtpScheduler *sched, RtpTimer *timer) {\n\tif (sched->thread_running) {\n\t\tortp_warning(\"Cannot change timer while the scheduler is running !!\");\n\t\treturn;\n\t}\n\tsched->timer = timer;\n\t/* report the timer increment */\n\tsched->timer_inc = (timer->interval.tv_usec / 1000) + (timer->interval.tv_sec * 1000);\n}\n\nvoid rtp_scheduler_start(RtpScheduler *sched) {\n\tif (sched->thread_running == 0) {\n\t\tsched->thread_running = 1;\n\t\tortp_mutex_lock(&sched->lock);\n\t\tortp_thread_create(&sched->thread, NULL, rtp_scheduler_schedule, (void *)sched);\n\t\tortp_cond_wait(&sched->unblock_select_cond, &sched->lock);\n\t\tortp_mutex_unlock(&sched->lock);\n\t} else ortp_warning(\"Scheduler thread already running.\");\n}\nvoid rtp_scheduler_stop(RtpScheduler *sched) {\n\tif (sched->thread_running == 1) {\n\t\tsched->thread_running = 0;\n\t\tortp_thread_join(sched->thread, NULL);\n\t} else ortp_warning(\"Scheduler thread is not running.\");\n}\n\nvoid rtp_scheduler_destroy(RtpScheduler *sched) {\n\tif (sched->thread_running) rtp_scheduler_stop(sched);\n\tortp_mutex_destroy(&sched->lock);\n\t// g_mutex_free(sched->unblock_select_mutex);\n\tortp_cond_destroy(&sched->unblock_select_cond);\n\tortp_free(sched);\n}\n\nvoid *rtp_scheduler_schedule(void *psched) {\n\tRtpScheduler *sched = (RtpScheduler *)psched;\n\tRtpTimer *timer = sched->timer;\n\tRtpSession *current;\n\n\t/* take this lock to prevent the thread to start until g_thread_create() returns\n\t    because we need sched->thread to be initialized */\n\tortp_mutex_lock(&sched->lock);\n\tortp_cond_signal(&sched->unblock_select_cond); /* unblock the starting thread */\n\tortp_mutex_unlock(&sched->lock);\n\ttimer->timer_init();\n\twhile (sched->thread_running) {\n\t\t/* do the processing here: */\n\t\tortp_mutex_lock(&sched->lock);\n\n\t\tcurrent = sched->list;\n\t\t/* processing all scheduled rtp sessions */\n\t\twhile (current != NULL) {\n\t\t\tortp_debug(\"scheduler: processing session=0x%p.\\n\", current);\n\t\t\trtp_session_process(current, sched->time_, sched);\n\t\t\tcurrent = current->next;\n\t\t}\n\t\t/* wake up all the threads that are sleeping in _select()  */\n\t\tortp_cond_broadcast(&sched->unblock_select_cond);\n\t\tortp_mutex_unlock(&sched->lock);\n\n\t\t/* now while the scheduler is going to sleep, the other threads can compute their\n\t\tresult mask and see if they have to leave, or to wait for next tick*/\n\t\t// ortp_message(\"scheduler: sleeping.\");\n\t\ttimer->timer_do();\n\t\tsched->time_ += sched->timer_inc;\n\t}\n\t/* when leaving the thread, stop the timer */\n\ttimer->timer_uninit();\n\treturn NULL;\n}\n\nvoid rtp_scheduler_add_session(RtpScheduler *sched, RtpSession *session) {\n\tRtpSession *oldfirst;\n\tint i;\n\tif (session->flags & RTP_SESSION_IN_SCHEDULER) {\n\t\t/* the rtp session is already scheduled, so return silently */\n\t\treturn;\n\t}\n\trtp_scheduler_lock(sched);\n\t/* enqueue the session to the list of scheduled sessions */\n\toldfirst = sched->list;\n\tsched->list = session;\n\tsession->next = oldfirst;\n\tif (sched->max_sessions == 0) {\n\t\tortp_error(\"rtp_scheduler_add_session: max_session=0 !\");\n\t}\n\t/* find a free pos in the session mask*/\n\tfor (i = 0; i < sched->max_sessions; i++) {\n\t\tif (!ORTP_FD_ISSET(i, &sched->all_sessions.rtpset)) {\n\t\t\tsession->mask_pos = i;\n\t\t\tsession_set_set(&sched->all_sessions, session);\n\t\t\t/* make a new session scheduled not blockable if it has not started*/\n\t\t\tif (session->flags & RTP_SESSION_RECV_NOT_STARTED) session_set_set(&sched->r_sessions, session);\n\t\t\tif (session->flags & RTP_SESSION_SEND_NOT_STARTED) session_set_set(&sched->w_sessions, session);\n\t\t\tif (i > sched->all_max) {\n\t\t\t\tsched->all_max = i;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\trtp_session_set_flag(session, RTP_SESSION_IN_SCHEDULER);\n\trtp_scheduler_unlock(sched);\n}\n\nvoid rtp_scheduler_remove_session(RtpScheduler *sched, RtpSession *session) {\n\tRtpSession *tmp;\n\tint cond = 1;\n\treturn_if_fail(session != NULL);\n\tif (!(session->flags & RTP_SESSION_IN_SCHEDULER)) {\n\t\t/* the rtp session is not scheduled, so return silently */\n\t\treturn;\n\t}\n\n\trtp_scheduler_lock(sched);\n\ttmp = sched->list;\n\tif (tmp == session) {\n\t\tsched->list = tmp->next;\n\t\trtp_session_unset_flag(session, RTP_SESSION_IN_SCHEDULER);\n\t\tsession_set_clr(&sched->all_sessions, session);\n\t\trtp_scheduler_unlock(sched);\n\t\treturn;\n\t}\n\t/* go the position of session in the list */\n\twhile (cond) {\n\t\tif (tmp != NULL) {\n\t\t\tif (tmp->next == session) {\n\t\t\t\ttmp->next = tmp->next->next;\n\t\t\t\tcond = 0;\n\t\t\t} else tmp = tmp->next;\n\t\t} else {\n\t\t\t/* the session was not found ! */\n\t\t\tortp_warning(\"rtp_scheduler_remove_session: the session was not found in the scheduler list!\");\n\t\t\tcond = 0;\n\t\t}\n\t}\n\trtp_session_unset_flag(session, RTP_SESSION_IN_SCHEDULER);\n\t/* delete the bit in the mask */\n\tsession_set_clr(&sched->all_sessions, session);\n\trtp_scheduler_unlock(sched);\n}\n"
  },
  {
    "path": "src/scheduler.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef SCHEDULER_H\n#define SCHEDULER_H\n\n#include \"ortp/rtpsession.h\"\n#include \"ortp/sessionset.h\"\n#include \"rtptimer.h\"\n\nstruct _RtpScheduler {\n\n\tRtpSession *list;        /* list of scheduled sessions*/\n\tSessionSet all_sessions; /* mask of scheduled sessions */\n\tint all_max;             /* the highest pos in the all mask */\n\tSessionSet r_sessions;   /* mask of sessions that have a recv event */\n\tint r_max;\n\tSessionSet w_sessions; /* mask of sessions that have a send event */\n\tint w_max;\n\tSessionSet e_sessions; /* mask of session that have error event */\n\tint e_max;\n\tint max_sessions; /* the number of position in the masks */\n\t                  /* GMutex  *unblock_select_mutex; */\n\tortp_cond_t unblock_select_cond;\n\tortp_mutex_t lock;\n\tortp_thread_t thread;\n\tint thread_running;\n\tstruct _RtpTimer *timer;\n\tuint32_t time_;     /*number of miliseconds elapsed since the start of the thread */\n\tuint32_t timer_inc; /* the timer increment in milisec */\n};\n\ntypedef struct _RtpScheduler RtpScheduler;\n\nRtpScheduler *rtp_scheduler_new(void);\nvoid rtp_scheduler_set_timer(RtpScheduler *sched, RtpTimer *timer);\nvoid rtp_scheduler_start(RtpScheduler *sched);\nvoid rtp_scheduler_stop(RtpScheduler *sched);\nvoid rtp_scheduler_destroy(RtpScheduler *sched);\n\nvoid rtp_scheduler_add_session(RtpScheduler *sched, RtpSession *session);\nvoid rtp_scheduler_remove_session(RtpScheduler *sched, RtpSession *session);\n\nvoid *rtp_scheduler_schedule(void *sched);\n\n#define rtp_scheduler_lock(sched) ortp_mutex_lock(&(sched)->lock)\n#define rtp_scheduler_unlock(sched) ortp_mutex_unlock(&(sched)->lock)\n\n/* void rtp_scheduler_add_set(RtpScheduler *sched, SessionSet *set); */\n\nORTP_PUBLIC RtpScheduler *ortp_get_scheduler(void);\n#endif\n"
  },
  {
    "path": "src/sessionset.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"scheduler.h\"\n#include <ortp/ortp.h>\n#include <ortp/sessionset.h>\n\n/**\n * Allocates and initialize a new empty session set.\n *\n * @return the empty session set.\n **/\nSessionSet *session_set_new(void) {\n\tSessionSet *set = (SessionSet *)ortp_malloc(sizeof(SessionSet));\n\tsession_set_init(set);\n\treturn set;\n}\n\n/**\n * Destroys a session set.\n *\n **/\n\nvoid session_set_destroy(SessionSet *set) {\n\tortp_free(set);\n}\n\nint count_power_items_simple(uint32_t v) {\n\tint c = 0, j;\n\tfor (j = 0; j < 32; j++) {\n\t\tif (((v) >> j) & 1) {\n\t\t\t++c;\n\t\t}\n\t}\n\treturn c;\n}\n\nint count_power_items_fast(uint32_t v) {\n\tint c = 0;\n\twhile (v) {\n\t\tc += (v & 1);\n\t\tv >>= 1;\n\t}\n\treturn c;\n}\n\nint session_set_and(SessionSet *sched_set, int maxs, SessionSet *user_set, SessionSet *result_set) {\n\tuint32_t *mask1, *mask2, *mask3;\n\tint i = 0;\n\tint ret = 0;\n\tmask1 = (uint32_t *)(void *)&sched_set->rtpset;\n\tmask2 = (uint32_t *)(void *)&user_set->rtpset;\n\tmask3 = (uint32_t *)(void *)&result_set->rtpset;\n\twhile (i < maxs + 1) {\n\t\t*mask3 = (*mask1) & (*mask2); /* computes the AND between the two masks*/\n\t\t/* and unset the sessions that have been found from the sched_set */\n\t\t*mask1 = (*mask1) & (~(*mask3));\n\t\tret += count_power_items_fast(*mask3);\n\t\ti += 32;\n\t\tmask1++;\n\t\tmask2++;\n\t\tmask3++;\n\t}\n\t// printf(\"session_set_and: ret=%i\\n\",ret);\n\treturn ret;\n}\n\n/**\n *\tThis function performs similarly as libc select() function, but performs on RtpSession\n *\tinstead of file descriptors.\n *\tsession_set_select() suspends the calling process until some events arrive on one of the\n *\tthree sets passed in argument. Two of the sets can be NULL.\n *\tThe first set \\a recvs is interpreted as a set of RtpSession waiting for receive events:\n *\ta new buffer (perhaps empty) is availlable on one or more sessions of the set, or the last\n *\treceive operation with rtp_session_recv_with_ts() would have finished if it were in\n *\tblocking mode.\n *\tThe second set is interpreted as a set of RtpSession waiting for send events, i.e. the last\n *\trtp_session_send_with_ts() call on a session would have finished if it were in blocking mode.\n *\n *\tWhen some events arrived on some of sets, then the function returns and sets are changed\n *\tto indicate the sessions where events happened.\n *\tSessions can be added to sets using session_set_set(), a session has to be tested to be\n *\tpart of a set using session_set_is_set().\n *\n * @param recvs a set of rtp sessions to be watched for read events\n * @param sends a set of rtp sessions to be watched for write events\n * @param errors a set of rtp sessions to be watched for errors\n * @return: the number of sessions on which the selected events happened.\n **/\nint session_set_select(SessionSet *recvs, SessionSet *sends, SessionSet *errors) {\n\tint ret = 0, bits;\n\tSessionSet temp;\n\tRtpScheduler *sched = ortp_get_scheduler();\n\n\t/*lock the scheduler to not read the masks while they are being modified by the scheduler*/\n\trtp_scheduler_lock(sched);\n\n\twhile (1) {\n\t\t/* computes the SessionSet intersection (in the other words mask intersection) between\n\t\tthe mask given by the user and scheduler masks */\n\t\tif (recvs != NULL) {\n\t\t\tsession_set_init(&temp);\n\t\t\tbits = session_set_and(&sched->r_sessions, sched->all_max, recvs, &temp);\n\t\t\tret += bits;\n\t\t\t/* copy the result set in the given user set (might be empty) */\n\t\t\tif (ret > 0) session_set_copy(recvs, &temp);\n\t\t}\n\t\tif (sends != NULL) {\n\t\t\tsession_set_init(&temp);\n\t\t\tbits = session_set_and(&sched->w_sessions, sched->all_max, sends, &temp);\n\t\t\tret += bits;\n\t\t\tif (ret > 0) {\n\t\t\t\t/* copy the result set in the given user set (might be empty)*/\n\t\t\t\tsession_set_copy(sends, &temp);\n\t\t\t}\n\t\t}\n\t\tif (errors != NULL) {\n\t\t\tsession_set_init(&temp);\n\t\t\tbits = session_set_and(&sched->e_sessions, sched->all_max, errors, &temp);\n\t\t\tret += bits;\n\t\t\tif (ret > 0) {\n\t\t\t\t/* copy the result set in the given user set */\n\t\t\t\tsession_set_copy(errors, &temp);\n\t\t\t}\n\t\t}\n\t\tif (ret > 0) {\n\t\t\t/* there are set file descriptors, return immediately */\n\t\t\t// printf(\"There are %i sessions set, returning.\\n\",ret);\n\t\t\trtp_scheduler_unlock(sched);\n\t\t\treturn ret;\n\t\t}\n\t\t// printf(\"There are %i sessions set.\\n\",ret);\n\t\t/* else we wait until the next loop of the scheduler*/\n\t\tortp_cond_wait(&sched->unblock_select_cond, &sched->lock);\n\t}\n\n\treturn -1;\n}\n\nint session_set_timedselect(SessionSet *recvs, SessionSet *sends, SessionSet *errors, struct timeval *timeout) {\n\tint ret = 0, bits;\n\tint remainingTime; // duration in ms\n\tSessionSet temp;\n\tRtpScheduler *sched;\n\tif (timeout == NULL) return session_set_select(recvs, sends, errors);\n\tsched = ortp_get_scheduler();\n\tremainingTime = timeout->tv_usec / 1000 + timeout->tv_sec * 1000;\n\n\t/*lock the scheduler to not read the masks while they are being modified by the scheduler*/\n\trtp_scheduler_lock(sched);\n\n\tdo {\n\t\t/* computes the SessionSet intersection (in the other words mask intersection) between\n\t\tthe mask given by the user and scheduler masks */\n\t\tif (recvs != NULL) {\n\t\t\tsession_set_init(&temp);\n\t\t\tbits = session_set_and(&sched->r_sessions, sched->all_max, recvs, &temp);\n\t\t\tret += bits;\n\t\t\t/* copy the result set in the given user set (might be empty) */\n\t\t\tif (ret > 0) session_set_copy(recvs, &temp);\n\t\t}\n\t\tif (sends != NULL) {\n\t\t\tsession_set_init(&temp);\n\t\t\tbits = session_set_and(&sched->w_sessions, sched->all_max, sends, &temp);\n\t\t\tret += bits;\n\t\t\tif (ret > 0) {\n\t\t\t\t/* copy the result set in the given user set (might be empty)*/\n\t\t\t\tsession_set_copy(sends, &temp);\n\t\t\t}\n\t\t}\n\t\tif (errors != NULL) {\n\t\t\tsession_set_init(&temp);\n\t\t\tbits = session_set_and(&sched->e_sessions, sched->all_max, errors, &temp);\n\t\t\tret += bits;\n\t\t\tif (ret > 0) {\n\t\t\t\t/* copy the result set in the given user set */\n\t\t\t\tsession_set_copy(errors, &temp);\n\t\t\t}\n\t\t}\n\t\tif (ret > 0) {\n\t\t\t/* there are set file descriptors, return immediately */\n\t\t\t// printf(\"There are %i sessions set, returning.\\n\",ret);\n\t\t\trtp_scheduler_unlock(sched);\n\t\t\treturn ret;\n\t\t}\n\t\t// printf(\"There are %i sessions set.\\n\",ret);\n\t\t/* else we wait until the next loop of the scheduler*/\n\t\tortp_cond_wait(&sched->unblock_select_cond, &sched->lock);\n\t\tremainingTime -= sched->timer_inc;\n\t} while (remainingTime > 0);\n\trtp_scheduler_unlock(sched);\n\n\treturn -1;\n}\n"
  },
  {
    "path": "src/str_utils.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <bctoolbox/defs.h>\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n\n#include \"ortp/ortp.h\"\n#include \"ortp/str_utils.h\"\n#include \"utils.h\"\n\nvoid qinit(queue_t *q) {\n\tmblk_init(&q->_q_stopper);\n\tq->_q_stopper.b_next = &q->_q_stopper;\n\tq->_q_stopper.b_prev = &q->_q_stopper;\n\tq->q_mcount = 0;\n}\n\nvoid mblk_init(mblk_t *mp) {\n\tmemset(mp, 0, sizeof(mblk_t));\n}\n\nvoid mblk_meta_copy(const mblk_t *source, mblk_t *dest) {\n\tdest->reserved1 = source->reserved1;\n\tdest->reserved2 = source->reserved2;\n\tmemcpy(&dest->net_addr, &source->net_addr, source->net_addrlen);\n\tdest->net_addrlen = source->net_addrlen;\n\tdest->timestamp = source->timestamp;\n\tdest->ttl_or_hl = source->ttl_or_hl;\n}\n\nunsigned char *dblk_base(dblk_t *db) {\n\treturn db->db_base;\n}\n\nunsigned char *dblk_lim(dblk_t *db) {\n\treturn db->db_lim;\n}\n\nmblk_t *allocb(size_t size, BCTBX_UNUSED(int pri)) {\n\tmblk_t *mp;\n\tdblk_t *datab;\n\n\tmp = (mblk_t *)ortp_malloc0(sizeof(mblk_t));\n\tdatab = dblk_alloc(size);\n\n\tmp->b_datap = datab;\n\tmp->b_rptr = mp->b_wptr = datab->db_base;\n\tmp->b_next = mp->b_prev = mp->b_cont = NULL;\n\treturn mp;\n}\n\nmblk_t *esballoc(uint8_t *buf, size_t size, BCTBX_UNUSED(int pri), void (*freefn)(void *)) {\n\tmblk_t *mp;\n\tdblk_t *datab;\n\n\tmp = (mblk_t *)ortp_malloc0(sizeof(mblk_t));\n\tdatab = dblk_alloc2(buf, size, freefn);\n\n\tmp->b_datap = datab;\n\tmp->b_rptr = mp->b_wptr = buf;\n\tmp->b_next = mp->b_prev = mp->b_cont = NULL;\n\treturn mp;\n}\n\nvoid freeb(mblk_t *mp) {\n\treturn_if_fail(mp->b_datap != NULL);\n\treturn_if_fail(mp->b_datap->db_base != NULL);\n\n\tdblk_unref(mp->b_datap);\n\tortp_free(mp);\n}\n\nvoid freemsg(mblk_t *mp) {\n\tmblk_t *tmp1, *tmp2;\n\ttmp1 = mp;\n\twhile (tmp1 != NULL) {\n\t\ttmp2 = tmp1->b_cont;\n\t\tfreeb(tmp1);\n\t\ttmp1 = tmp2;\n\t}\n}\n\nmblk_t *dupb(mblk_t *mp) {\n\tmblk_t *newm;\n\treturn_val_if_fail(mp->b_datap != NULL, NULL);\n\treturn_val_if_fail(mp->b_datap->db_base != NULL, NULL);\n\n\tdblk_ref(mp->b_datap);\n\tnewm = (mblk_t *)ortp_malloc0(sizeof(mblk_t));\n\tmblk_meta_copy(mp, newm);\n\tnewm->b_datap = mp->b_datap;\n\tnewm->b_rptr = mp->b_rptr;\n\tnewm->b_wptr = mp->b_wptr;\n\treturn newm;\n}\n\n/* duplicates a complex mblk_t */\nmblk_t *dupmsg(mblk_t *m) {\n\tmblk_t *newm = NULL, *mp, *prev;\n\tprev = newm = dupb(m);\n\tm = m->b_cont;\n\twhile (m != NULL) {\n\t\tmp = dupb(m);\n\t\tprev->b_cont = mp;\n\t\tprev = mp;\n\t\tm = m->b_cont;\n\t}\n\treturn newm;\n}\n\nvoid putq(queue_t *q, mblk_t *mp) {\n\tq->_q_stopper.b_prev->b_next = mp;\n\tmp->b_prev = q->_q_stopper.b_prev;\n\tmp->b_next = &q->_q_stopper;\n\tq->_q_stopper.b_prev = mp;\n\tq->q_mcount++;\n}\n\nmblk_t *getq(queue_t *q) {\n\tmblk_t *tmp;\n\ttmp = q->_q_stopper.b_next;\n\tif (tmp == &q->_q_stopper) return NULL;\n\tq->_q_stopper.b_next = tmp->b_next;\n\ttmp->b_next->b_prev = &q->_q_stopper;\n\ttmp->b_prev = NULL;\n\ttmp->b_next = NULL;\n\tq->q_mcount--;\n\treturn tmp;\n}\n\nmblk_t *peekq(queue_t *q) {\n\tmblk_t *tmp;\n\ttmp = q->_q_stopper.b_next;\n\tif (tmp == &q->_q_stopper) return NULL;\n\treturn tmp;\n}\n\n/* insert mp in q just before emp */\nvoid insq(queue_t *q, mblk_t *emp, mblk_t *mp) {\n\tif (emp == NULL) {\n\t\tputq(q, mp);\n\t\treturn;\n\t}\n\tq->q_mcount++;\n\temp->b_prev->b_next = mp;\n\tmp->b_prev = emp->b_prev;\n\temp->b_prev = mp;\n\tmp->b_next = emp;\n}\n\nvoid remq(queue_t *q, mblk_t *mp) {\n\tq->q_mcount--;\n\tmp->b_prev->b_next = mp->b_next;\n\tmp->b_next->b_prev = mp->b_prev;\n\tmp->b_next = NULL;\n\tmp->b_prev = NULL;\n}\n\n/* remove and free all messages in the q */\nvoid flushq(queue_t *q, BCTBX_UNUSED(int how)) {\n\tmblk_t *mp;\n\n\twhile ((mp = getq(q)) != NULL) {\n\t\tfreemsg(mp);\n\t}\n}\n\nsize_t msgdsize(const mblk_t *mp) {\n\tsize_t msgsize = 0;\n\twhile (mp != NULL) {\n\t\tmsgsize += (size_t)(mp->b_wptr - mp->b_rptr);\n\t\tmp = mp->b_cont;\n\t}\n\treturn msgsize;\n}\n\nvoid msgpullup(mblk_t *mp, size_t len) {\n\tmblk_t *firstm = mp;\n\tdblk_t *db;\n\tsize_t wlen = 0;\n\tunsigned char *base;\n\n\tif (mp->b_cont == NULL) {\n\t\t/* Special case optimisations */\n\t\tif (len == (size_t)-1) return; /*nothing to do, message is not fragmented. */\n\t\tif (mp->b_datap->db_base + len <= mp->b_datap->db_lim) {\n\t\t\t/* The underlying data block is larger than the requested size, nothing to do. */\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (len == (size_t)-1) len = msgdsize(mp);\n\tdb = dblk_alloc(len);\n\tbase = db->db_base;\n\twhile (wlen < len && mp != NULL) {\n\t\tint remain = (int)(len - wlen);\n\t\tint mlen = (int)(mp->b_wptr - mp->b_rptr);\n\t\tif (mlen <= remain) {\n\t\t\tmemcpy(&base[wlen], mp->b_rptr, mlen);\n\t\t\twlen += mlen;\n\t\t\tmp = mp->b_cont;\n\t\t} else {\n\t\t\tmemcpy(&base[wlen], mp->b_rptr, remain);\n\t\t\twlen += remain;\n\t\t}\n\t}\n\t/*set firstm to point to the new datab */\n\tfreemsg(firstm->b_cont);\n\tfirstm->b_cont = NULL;\n\tdblk_unref(firstm->b_datap);\n\tfirstm->b_datap = db;\n\tfirstm->b_rptr = db->db_base;\n\tfirstm->b_wptr = firstm->b_rptr + wlen;\n}\n\n/* pullup message but insert an insert_size zeroised buffer at offset */\n/* final size will be current size + insert size\n * b->w_bptr is set at the end of the message even if the insertion if performed at the end of the message */\nvoid msgpullup_with_insert(mblk_t *mp, size_t offset, size_t insert_size) {\n\tmblk_t *firstm = mp;\n\tdblk_t *db;\n\tsize_t wlen = 0;\n\tsize_t len = msgdsize(mp);\n\tif (offset >= len) {\n\t\tmsgpullup(mp, len + insert_size); /* we want to insert it at the end, that's what regular pullup does */\n\t\tmp->b_wptr += offset - len + insert_size;\n\t\treturn;\n\t}\n\n\tlen += insert_size;\n\tdb = dblk_alloc(len);\n\n\twhile (mp != NULL) { /* copy the whole original content, we do not crop as in regular pullup */\n\t\tint mlen = (int)(mp->b_wptr - mp->b_rptr);\n\t\tif (wlen + mlen <= offset ||\n\t\t    wlen > offset) { /* this whole chunk fit before the blank insert or we already made the insert */\n\t\t\tmemcpy(&db->db_base[wlen], mp->b_rptr, mlen);\n\t\t\twlen += mlen;\n\t\t} else {\n\t\t\t/* split this chunk */\n\t\t\tmemcpy(&db->db_base[wlen], mp->b_rptr, offset - wlen); /* write up to offset */\n\t\t\tmemset(&db->db_base[offset], 0, insert_size);          /* zeroise the inserted part */\n\t\t\tmemcpy(&db->db_base[offset + insert_size], mp->b_rptr + offset - wlen,\n\t\t\t       wlen + mlen - offset); /* copy the rest of this chunk */\n\t\t\twlen += mlen + insert_size;\n\t\t}\n\t\tmp = mp->b_cont;\n\t}\n\n\tfreemsg(firstm->b_cont);\n\tfirstm->b_cont = NULL;\n\tdblk_unref(firstm->b_datap);\n\tfirstm->b_datap = db;\n\tfirstm->b_rptr = db->db_base;\n\tfirstm->b_wptr = firstm->b_rptr + wlen;\n}\n\nmblk_t *copyb(const mblk_t *mp) {\n\tmblk_t *newm;\n\tint len = (int)(mp->b_wptr - mp->b_rptr);\n\tnewm = allocb(len, BPRI_MED);\n\tmemcpy(newm->b_wptr, mp->b_rptr, len);\n\tnewm->b_wptr += len;\n\tmemcpy(&newm->recv_addr, &mp->recv_addr, sizeof(newm->recv_addr));\n\treturn newm;\n}\n\nmblk_t *copymsg(const mblk_t *mp) {\n\tmblk_t *newm = 0, *m;\n\tm = newm = copyb(mp);\n\tmp = mp->b_cont;\n\twhile (mp != NULL) {\n\t\tm->b_cont = copyb(mp);\n\t\tm = m->b_cont;\n\t\tmp = mp->b_cont;\n\t}\n\treturn newm;\n}\n\nmblk_t *appendb(mblk_t *mp, const char *data, size_t size, bool_t pad) {\n\tsize_t padcnt = 0;\n\tsize_t i;\n\tunsigned char *lim;\n\tif (pad) {\n\t\tpadcnt = (size_t)(4 - ((((intptr_t)mp->b_wptr) + size) % 4)) % 4;\n\t}\n\tlim = mp->b_datap->db_lim;\n\tif ((mp->b_wptr + size + padcnt) > lim) {\n\t\t/* buffer is not large enough: append a new block (with the same size ?)*/\n\t\tsize_t plen = (size_t)((char *)lim - (char *)mp->b_datap->db_base);\n\t\tmp->b_cont = allocb(MAX(plen, size), 0);\n\t\tmp = mp->b_cont;\n\t}\n\tif (size) memcpy(mp->b_wptr, data, size);\n\tmp->b_wptr += size;\n\tfor (i = 0; i < padcnt; i++) {\n\t\tmp->b_wptr[0] = 0;\n\t\tmp->b_wptr++;\n\t}\n\treturn mp;\n}\n\nvoid msgappend(mblk_t *mp, const char *data, size_t size, bool_t pad) {\n\twhile (mp->b_cont != NULL)\n\t\tmp = mp->b_cont;\n\tappendb(mp, data, size, pad);\n}\n\nmblk_t *concatb(mblk_t *mp, mblk_t *newm) {\n\twhile (mp->b_cont != NULL)\n\t\tmp = mp->b_cont;\n\tmp->b_cont = newm;\n\twhile (newm->b_cont != NULL)\n\t\tnewm = newm->b_cont;\n\treturn newm;\n}\n\nvoid msgb_allocator_init(msgb_allocator_t *a) {\n\tqinit(&a->q);\n\ta->max_blocks = 0; /* no limit */\n}\n\nvoid msgb_allocator_set_max_blocks(msgb_allocator_t *pa, int max_blocks) {\n\tpa->max_blocks = max_blocks;\n}\n\nstatic void msgb_allocator_free_db(BCTBX_UNUSED(void *unused)) {\n}\n\nmblk_t *msgb_allocator_alloc(msgb_allocator_t *a, size_t size) {\n\tqueue_t *q = &a->q;\n\tmblk_t *m, *found = NULL;\n\tint busy_blocks = 0;\n\n\t/*lookup for an unused msgb (data block with ref count ==1)*/\n\tfor (m = qbegin(q); !qend(q, m); m = qnext(q, m)) {\n\t\tif ((size_t)(m->b_datap->db_lim - m->b_datap->db_base) >= size) {\n\t\t\tif (dblk_ref_value(m->b_datap) == 1) {\n\t\t\t\tfound = m;\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tbusy_blocks++;\n\t\t\t}\n\t\t}\n\t}\n\tif (a->max_blocks != 0 && busy_blocks >= a->max_blocks) {\n\t\treturn NULL;\n\t}\n\tif (found == NULL) {\n\t\tfound = allocb(size, 0);\n\t\t/*Hack: we put a special freefn impletation to be able to recognize mblk_t allocated by the msgb_allocator_t */\n\t\tfound->b_datap->db_freefn = msgb_allocator_free_db;\n\t\tputq(q, found);\n\t}\n\treturn dupb(found);\n}\n\nvoid msgb_allocator_uninit(msgb_allocator_t *a) {\n\tflushq(&a->q, -1);\n}\n\n/*Same as ownb(), but invoke it for each mblk_t of the chain*/\nmblk_t *msgown(mblk_t *mp) {\n\tint single_owner_ref = (mp->b_datap->db_freefn == msgb_allocator_free_db) ? 2 : 1;\n\n\tif (dblk_ref_value(mp->b_datap) > single_owner_ref) {\n\t\t// ortp_message(\"msgown(): datab copied db_ref=%i  single_owner_ref=%i\", dblk_ref_value(mp->b_datap),\n\t\t// single_owner_ref);\n\t\tmsgpullup(mp, msgdsize(mp));\n\t}\n\treturn mp;\n}\n\nvoid ortp_recvaddr_to_sockaddr(ortp_recv_addr_t *recvaddr, struct sockaddr *addr, socklen_t *socklen) {\n\tif (recvaddr->family == AF_INET) {\n\t\tstruct sockaddr_in *addr_in = (struct sockaddr_in *)addr;\n\t\taddr_in->sin_family = AF_INET;\n\t\taddr_in->sin_addr = recvaddr->addr.ipi_addr;\n\t\taddr_in->sin_port = recvaddr->port;\n\t\t*socklen = sizeof(struct sockaddr_in);\n\t} else if (recvaddr->family == AF_INET6) {\n\t\tstruct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr;\n\t\taddr_in6->sin6_family = AF_INET6;\n\t\taddr_in6->sin6_port = recvaddr->port;\n\t\tmemcpy(&addr_in6->sin6_addr, &recvaddr->addr.ipi6_addr, sizeof(recvaddr->addr.ipi6_addr));\n\t\t*socklen = sizeof(struct sockaddr_in6);\n\t} else {\n\t\t*socklen = 0;\n\t}\n}\nvoid ortp_sockaddr_to_recvaddr(const struct sockaddr *addr, ortp_recv_addr_t *recvaddr) {\n\tif (addr->sa_family == AF_INET) {\n\t\tstruct sockaddr_in *addr_in = (struct sockaddr_in *)addr;\n\t\trecvaddr->family = AF_INET;\n\t\trecvaddr->port = addr_in->sin_port;\n\t\trecvaddr->addr.ipi_addr = addr_in->sin_addr;\n\t} else if (addr->sa_family == AF_INET6) {\n\t\tstruct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr;\n\t\trecvaddr->family = AF_INET6;\n\t\trecvaddr->port = addr_in6->sin6_port;\n\t\tmemcpy(&recvaddr->addr.ipi6_addr, &addr_in6->sin6_addr, sizeof(addr_in6->sin6_addr));\n\t}\n}\n"
  },
  {
    "path": "src/telephonyevents.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <bctoolbox/defs.h>\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include \"ortp/str_utils.h\"\n#include \"rtpsession_priv.h\"\n#include \"utils.h\"\n#include <bctoolbox/port.h>\n#include <ortp/ortp.h>\n#include <ortp/telephonyevents.h>\n\nPayloadType payload_type_telephone_event = {\n    PAYLOAD_AUDIO_PACKETIZED, /*type */\n    8000,                     /*clock rate */\n    0,                        /* bytes per sample N/A */\n    NULL,                     /* zero pattern N/A*/\n    0,                        /*pattern_length N/A */\n    0,                        /*\tnormal_bitrate */\n    \"telephone-event\",        /* MIME subtype */\n    1,                        /* Audio Channels */\n    0                         /*flags */\n};\n\n/**\n * Tells whether telephony events payload type is supported within the\n * context of the rtp session.\n * @param session a rtp session\n *\n * @return the payload type number used for telephony events if found, -1 if not found.\n **/\nint rtp_session_telephone_events_supported(RtpSession *session) {\n\t/* search for a telephony event payload in the current profile */\n\treturn rtp_profile_get_payload_number_from_mime(session->snd.profile, \"telephone-event\");\n}\n\nbool_t rtp_profile_is_telephone_event(const RtpProfile *prof, int pt_num) {\n\tPayloadType *pt = rtp_profile_get_payload(prof, pt_num);\n\treturn pt && strcasecmp(pt->mime_type, \"telephone-event\") == 0;\n}\n\n/**\n * Tells whether telephone event payload type is supported for send within the\n * context of the rtp session.\n * @param session a rtp session\n *\n * @return the payload type number used for telephony events if found, -1 if not found.\n **/\nint rtp_session_send_telephone_events_supported(RtpSession *session) {\n\t/* search for a telephony event payload in the current profile */\n\treturn rtp_profile_get_payload_number_from_mime(session->snd.profile, \"telephone-event\");\n}\n\n/**\n * Tells whether telephone event payload type is supported for receiving within the\n * context of the rtp session.\n * @param session a rtp session\n *\n * @return the payload type number used for telephony events if found, -1 if not found.\n **/\nint rtp_session_recv_telephone_events_supported(RtpSession *session) {\n\t/* search for a telephony event payload in the current profile */\n\treturn rtp_profile_get_payload_number_from_mime(session->rcv.profile, \"telephone-event\");\n}\n\n/**\n *\tAllocates a new rtp packet to be used to add named telephony events. The application can use\n *\tthen rtp_session_add_telephone_event() to add named events to the packet.\n *\tFinally the packet has to be sent with rtp_session_sendm_with_ts().\n *\n * @param session a rtp session.\n * @param start boolean to indicate if the marker bit should be set.\n *\n * @return a message block containing the rtp packet if successfull, NULL if the rtp session\n * cannot support telephony event (because the rtp profile it is bound to does not include\n * a telephony event payload type).\n **/\nmblk_t *rtp_session_create_telephone_event_packet(RtpSession *session, int start) {\n\tmblk_t *mp;\n\trtp_header_t *rtp;\n\tPayloadType *cur_pt = rtp_profile_get_payload(session->snd.profile, rtp_session_get_send_payload_type(session));\n\tint tev_pt = session->tev_send_pt;\n\n\tif (tev_pt != -1) {\n\t\tPayloadType *cur_tev_pt = rtp_profile_get_payload(session->snd.profile, tev_pt);\n\t\tif (!cur_tev_pt) {\n\t\t\tortp_error(\"Undefined telephone-event payload type %i choosen for sending telephone event\", tev_pt);\n\t\t\ttev_pt = -1;\n\t\t} else if (cur_pt && cur_tev_pt->clock_rate != cur_pt->clock_rate) {\n\t\t\tortp_warning(\"Telephone-event payload type %i has clockrate %i while main audio codec has clockrate %i: \"\n\t\t\t             \"this is not permitted.\",\n\t\t\t             tev_pt, cur_tev_pt->clock_rate, cur_pt->clock_rate);\n\t\t}\n\t}\n\n\tif (tev_pt == -1) {\n\t\ttev_pt = rtp_profile_find_payload_number(session->snd.profile, \"telephone-event\",\n\t\t                                         cur_pt ? cur_pt->clock_rate : 8000, 1);\n\t}\n\treturn_val_if_fail(tev_pt != -1, NULL);\n\n\tmp = allocb(RTP_FIXED_HEADER_SIZE + TELEPHONY_EVENTS_ALLOCATED_SIZE, BPRI_MED);\n\tif (mp == NULL) return NULL;\n\trtp = (rtp_header_t *)mp->b_rptr;\n\trtp->version = 2;\n\trtp->markbit = start;\n\trtp->padbit = 0;\n\trtp->extbit = 0;\n\trtp->cc = 0;\n\trtp_header_set_ssrc(rtp, session->snd.ssrc);\n\t/* timestamp set later, when packet is sended */\n\t/*seq number set later, when packet is sended */\n\n\t/*set the payload type */\n\trtp->paytype = tev_pt;\n\n\t/*copy the payload */\n\tmp->b_wptr += RTP_FIXED_HEADER_SIZE;\n\treturn mp;\n}\n\n/**\n *@param session a rtp session.\n *@param packet a rtp packet as a mblk_t\n *@param event the event type as described in rfc2833, ie one of the TEV_* macros.\n *@param end a boolean to indicate if the end bit should be set. (end of tone)\n *@param volume the volume of the telephony tone, as described in rfc2833\n *@param duration the duration of the telephony tone, in timestamp unit.\n *\n * Adds a named telephony event to a rtp packet previously allocated using\n * rtp_session_create_telephone_event_packet().\n *\n *@return 0 on success.\n **/\nint rtp_session_add_telephone_event(RtpSession *session,\n\t\t\tmblk_t *packet, uint8_t event, int end, uint8_t volume, uint16_t duration)\n{\n\tmblk_t *mp=packet;\n\ttelephone_event_t *event_hdr;\n\n\t(void)session;\n\t/* find the place where to add the new telephony event to the packet */\n\twhile (mp->b_cont != NULL)\n\t\tmp = mp->b_cont;\n\t/* see if we need to allocate a new mblk_t */\n\tif ((mp->b_wptr) >= mp->b_datap->db_lim) {\n\t\tmblk_t *newm = allocb(TELEPHONY_EVENTS_ALLOCATED_SIZE, BPRI_MED);\n\t\tmp->b_cont = newm;\n\t\tmp = mp->b_cont;\n\t}\n\tif (mp == NULL) return -1;\n\tevent_hdr = (telephone_event_t *)mp->b_wptr;\n\tevent_hdr->event = event;\n\tevent_hdr->R = 0;\n\tevent_hdr->E = end;\n\tevent_hdr->volume = volume;\n\tevent_hdr->duration = htons(duration);\n\tmp->b_wptr += sizeof(telephone_event_t);\n\treturn 0;\n}\n/**\n *\tThis functions creates telephony events packets for dtmf and sends them.\n *\tIt uses rtp_session_create_telephone_event_packet() and\n *\trtp_session_add_telephone_event() to create them and finally\n *\trtp_session_sendm_with_ts() to send them.\n *\n * @param session a rtp session\n * @param dtmf a character meaning the dtmf (ex: '1', '#' , '9' ...)\n * @param userts the timestamp\n * @return 0 if successfull, -1 if the session cannot support telephony events or if the dtmf given as argument is not\n *valid.\n **/\nint rtp_session_send_dtmf(RtpSession *session, char dtmf, uint32_t userts) {\n\treturn rtp_session_send_dtmf2(session, dtmf, userts, 480);\n}\n\n/**\n * A variation of rtp_session_send_dtmf() with duration specified.\n *\n * @param session a rtp session\n * @param dtmf a character meaning the dtmf (ex: '1', '#' , '9' ...)\n * @param userts the timestamp\n * @param duration duration of the dtmf in timestamp units\n * @return 0 if successfull, -1 if the session cannot support telephony events or if the dtmf given as argument is not\n *valid.\n **/\nint rtp_session_send_dtmf2(RtpSession *session, char dtmf, uint32_t userts, int duration) {\n\tmblk_t *m1, *m2, *m3;\n\tint tev_type;\n\tint durationtier = duration / 3;\n\n\t/* create the first telephony event packet */\n\tswitch (dtmf) {\n\t\tcase '1':\n\t\t\ttev_type = TEV_DTMF_1;\n\t\t\tbreak;\n\t\tcase '2':\n\t\t\ttev_type = TEV_DTMF_2;\n\t\t\tbreak;\n\t\tcase '3':\n\t\t\ttev_type = TEV_DTMF_3;\n\t\t\tbreak;\n\t\tcase '4':\n\t\t\ttev_type = TEV_DTMF_4;\n\t\t\tbreak;\n\t\tcase '5':\n\t\t\ttev_type = TEV_DTMF_5;\n\t\t\tbreak;\n\t\tcase '6':\n\t\t\ttev_type = TEV_DTMF_6;\n\t\t\tbreak;\n\t\tcase '7':\n\t\t\ttev_type = TEV_DTMF_7;\n\t\t\tbreak;\n\t\tcase '8':\n\t\t\ttev_type = TEV_DTMF_8;\n\t\t\tbreak;\n\t\tcase '9':\n\t\t\ttev_type = TEV_DTMF_9;\n\t\t\tbreak;\n\t\tcase '*':\n\t\t\ttev_type = TEV_DTMF_STAR;\n\t\t\tbreak;\n\t\tcase '0':\n\t\t\ttev_type = TEV_DTMF_0;\n\t\t\tbreak;\n\t\tcase '#':\n\t\t\ttev_type = TEV_DTMF_POUND;\n\t\t\tbreak;\n\n\t\tcase 'A':\n\t\tcase 'a':\n\t\t\ttev_type = TEV_DTMF_A;\n\t\t\tbreak;\n\n\t\tcase 'B':\n\t\tcase 'b':\n\t\t\ttev_type = TEV_DTMF_B;\n\t\t\tbreak;\n\n\t\tcase 'C':\n\t\tcase 'c':\n\t\t\ttev_type = TEV_DTMF_C;\n\t\t\tbreak;\n\n\t\tcase 'D':\n\t\tcase 'd':\n\t\t\ttev_type = TEV_DTMF_D;\n\t\t\tbreak;\n\n\t\tcase '!':\n\t\t\ttev_type = TEV_FLASH;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tortp_warning(\"Bad dtmf: %c.\", dtmf);\n\t\t\treturn -1;\n\t}\n\n\tm1 = rtp_session_create_telephone_event_packet(session, 1);\n\tif (m1 == NULL) return -1;\n\trtp_session_add_telephone_event(session, m1, tev_type, 0, 10, durationtier);\n\t/* create a second packet */\n\tm2 = rtp_session_create_telephone_event_packet(session, 0);\n\tif (m2 == NULL) return -1;\n\trtp_session_add_telephone_event(session, m2, tev_type, 0, 10, durationtier + durationtier);\n\n\t/* create a third and final packet */\n\tm3 = rtp_session_create_telephone_event_packet(session, 0);\n\tif (m3 == NULL) return -1;\n\trtp_session_add_telephone_event(session, m3, tev_type, 1, 10, duration);\n\n\t/* and now sends them */\n\trtp_session_sendm_with_ts(session, m1, userts);\n\trtp_session_sendm_with_ts(session, m2, userts);\n\t/* the last packet is sent three times in order to improve reliability*/\n\tm1 = copymsg(m3);\n\tm2 = copymsg(m3);\n\t/*\t\t\tNOTE:\t\t\t*/\n\t/* we need to copymsg() instead of dupmsg() because the buffers are modified when\n\tthe packet is sended because of the host-to-network conversion of timestamp,ssrc, csrc, and\n\tseq number.\n\t*/\n\trtp_session_sendm_with_ts(session, m3, userts);\n\tsession->rtp.snd_seq--;\n\trtp_session_sendm_with_ts(session, m1, userts);\n\tsession->rtp.snd_seq--;\n\trtp_session_sendm_with_ts(session, m2, userts);\n\treturn 0;\n}\n\n/**\n *\tReads telephony events from a rtp packet. \\a *tab points to the beginning of the event buffer.\n *\n * @param session a rtp session from which telephony events are received.\n * @param packet a rtp packet as a mblk_t.\n * @param tab the address of a pointer.\n * @return the number of events in the packet if successfull, 0 if the packet did not contain telephony events.\n **/\nint rtp_session_read_telephone_event(RtpSession *session, mblk_t *packet, telephone_event_t **tab) {\n\tint datasize;\n\tint num;\n\tint i;\n\ttelephone_event_t *tev;\n\trtp_header_t *hdr = (rtp_header_t *)packet->b_rptr;\n\tunsigned char *payload;\n\tif (!rtp_profile_is_telephone_event(session->rcv.profile, hdr->paytype)) return 0; /* this is not tel ev.*/\n\tdatasize = rtp_get_payload(packet, &payload);\n\ttev = *tab = (telephone_event_t *)payload;\n\t/* convert from network to host order what should be */\n\tnum = datasize / sizeof(telephone_event_t);\n\tfor (i = 0; i < num; i++) {\n\t\ttev[i].duration = ntohs(tev[i].duration);\n\t}\n\treturn num;\n}\n\nstatic void notify_tev(RtpSession *session, telephone_event_t *event) {\n\tOrtpEvent *ev;\n\tOrtpEventData *evd;\n\trtp_signal_table_emit2(&session->on_telephone_event, ORTP_INT_TO_POINTER(event[0].event));\n\tif (session->eventqs != NULL) {\n\t\tev = ortp_event_new(ORTP_EVENT_TELEPHONE_EVENT);\n\t\tevd = ortp_event_get_data(ev);\n\t\tevd->packet = dupmsg(session->current_tev);\n\t\tevd->info.telephone_event = event[0].event;\n\t\trtp_session_dispatch_event(session, ev);\n\t}\n}\n\nstatic void notify_events_ended(RtpSession *session, telephone_event_t *events, int num) {\n\tint i;\n\tfor (i = 0; i < num; i++) {\n\t\tif (events[i].E == 1) {\n\t\t\tnotify_tev(session, &events[i]);\n\t\t}\n\t}\n}\n\n/* for high level telephony event callback */\nvoid rtp_session_check_telephone_events(RtpSession *session, mblk_t *m0) {\n\ttelephone_event_t *events, *evbuf;\n\tint num, num2;\n\tint i;\n\trtp_header_t *hdr;\n\tmblk_t *cur_tev;\n\tunsigned char *payload;\n\tint datasize;\n\n\thdr = (rtp_header_t *)m0->b_rptr;\n\n\tdatasize = rtp_get_payload(m0, &payload);\n\n\tnum = datasize / sizeof(telephone_event_t);\n\tevents = (telephone_event_t *)payload;\n\n\tif (hdr->markbit == 1) {\n\t\t/* this is a start of new events. Store the event buffer for later use*/\n\t\tif (session->current_tev != NULL) {\n\t\t\tfreemsg(session->current_tev);\n\t\t\tsession->current_tev = NULL;\n\t\t}\n\t\tsession->current_tev = copymsg(m0);\n\t\t/* handle the case where the events are short enough to end within the packet that has the marker bit*/\n\t\tnotify_events_ended(session, events, num);\n\t}\n\t/* whatever there is a markbit set or not, we parse the packet and compare it to previously received one */\n\tcur_tev = session->current_tev;\n\tif (cur_tev != NULL) {\n\t\t/* first compare timestamp, they must be identical */\n\t\tif (rtp_get_timestamp(cur_tev) == rtp_get_timestamp(m0)) {\n\t\t\tdatasize = rtp_get_payload(cur_tev, &payload);\n\t\t\tnum2 = datasize / sizeof(telephone_event_t);\n\t\t\tevbuf = (telephone_event_t *)payload;\n\t\t\tfor (i = 0; i < MIN(num, num2); i++) {\n\t\t\t\tif (events[i].E == 1) {\n\t\t\t\t\t/* update events that have ended */\n\t\t\t\t\tif (evbuf[i].E == 0) {\n\t\t\t\t\t\tevbuf[i].E = 1;\n\t\t\t\t\t\t/* this is a end of event, report it */\n\t\t\t\t\t\tnotify_tev(session, &events[i]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t/* timestamp are not identical: this is not the same events*/\n\t\t\tif (session->current_tev != NULL) {\n\t\t\t\tfreemsg(session->current_tev);\n\t\t\t\tsession->current_tev = NULL;\n\t\t\t}\n\t\t\tsession->current_tev = copymsg(m0);\n\t\t\tnotify_events_ended(session, events, num);\n\t\t}\n\t} else {\n\t\t/* there is no pending events, but we did not received marked bit packet\n\t\teither the sending implementation is not compliant, either it has been lost,\n\t\twe must deal with it anyway.*/\n\t\tsession->current_tev = copymsg(m0);\n\t\t/* inform the application if there are tone ends */\n\t\tnotify_events_ended(session, events, num);\n\t}\n}\n"
  },
  {
    "path": "src/tests/.gitignore",
    "content": "Makefile\nMakefile.in\n.deps\n.libs\n*.lo\n*.la\nmrtprecv\nmrtpsend\nrtpmemtest\nrtprecv\nrtpsend\ntest_timer\ntevmrtprecv\ntevrtprecv\ntevrtpsend\nrtpsend_stupid\n"
  },
  {
    "path": "src/tests/CMakeLists.txt",
    "content": "############################################################################\n# Copyright (c) 2010-2022 Belledonne Communications SARL.\n#\n# This file is part of oRTP \n# (see https://gitlab.linphone.org/BC/public/ortp).\n#\n############################################################################\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as\n# published by the Free Software Foundation, either version 3 of the\n# License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. If not, see <http://www.gnu.org/licenses/>.\n############################################################################\n\nif (NOT IOS)\n\tset(EXECUTABLES rtpsend rtprecv mrtpsend mrtprecv test_timer tevrtpsend tevrtprecv rtpsend_stupid)\n\tforeach(executable ${EXECUTABLES})\n\t\tbc_apply_compile_flags(${executable}.c STRICT_OPTIONS_CPP STRICT_OPTIONS_C)\n\t\tadd_executable(${executable} ${executable}.c)\n\t\ttarget_link_libraries(${executable} ortp ${BCToolbox_TARGET})\n\tendforeach()\n\n\tinstall(TARGETS ${EXECUTABLES}\n\t\tRUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}\n\t\tPERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE\n\t)\nendif()\n"
  },
  {
    "path": "src/tests/Makefile.am",
    "content": "SUBDIRS=win_receiver win_sender\n\nif ENABLE_TESTS\n\nnoinst_PROGRAMS=rtpsend rtprecv mrtpsend mrtprecv test_timer rtpmemtest tevrtpsend tevrtprecv tevmrtprecv rtpsend_stupid fmtp_parse\n\nrtpsend_SOURCES=rtpsend.c\n\nrtprecv_SOURCES=rtprecv.c\n\nmrtpsend_SOURCES=mrtpsend.c\n\nmrtprecv_SOURCES=mrtprecv.c\n\nrtpmemtest_SOURCES=rtpmemtest.c\n\ntest_timer_SOURCES=test_timer.c\n\ntevrtpsend_SOURCES=tevrtpsend.c\n\ntevrtprecv_SOURCES=tevrtprecv.c\n\ntevmrtprecv_SOURCES=tevmrtprecv.c\n\nrtpsend_stupid_SOURCES=rtpsend_stupid.c\n\nfmtp_parse_SOURCES=fmtpparse.c\n\nendif\n\nAM_CPPFLAGS=-I$(top_srcdir)/include/\nAM_CFLAGS=-D_ORTP_SOURCE $(PTHREAD_CFLAGS) \nAM_LDFLAGS=$(PTHREAD_LDFLAGS)\nLDADD=$(top_builddir)/src/libortp.la $(BCTOOLBOX_LIBS)\n"
  },
  {
    "path": "src/tests/fmtpparse.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <ortp/ortp.h>\n\nint main(int argc, char *argv[]) {\n\tchar value[256] = {0};\n\tif (argc < 3) {\n\t\tfprintf(stderr, \"%s <fmtp-line> <param-to-extract>\\n\", argv[0]);\n\t\treturn -1;\n\t}\n\tif (fmtp_get_value(argv[1], argv[2], value, sizeof(value))) {\n\t\tprintf(\"%s\\n\", value);\n\t} else {\n\t\tfprintf(stderr, \"No such parameter\\n\");\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/mrtprecv.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* this program shows how to receive streams in paralel using the SessionSet api\n    and two threads only. */\n\n#include <assert.h>\n#include <fcntl.h>\n#include <ortp/ortp.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#ifndef _WIN32\n#include <unistd.h>\n\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#endif\n\nint runcond = 1;\n\nvoid stophandler(int signum) {\n\truncond = 0;\n}\n\nstatic char *help = \"usage: mrtprecv\tfile_prefix local_port number_of_streams \\n\"\n                    \"Receives multiples rtp streams on local_port+2*k, k={0..number_of_streams}\\n\";\n\n#define STREAMS_COUNT 1000\n\n/* malloc'd in order to detect buffer overflows with efence */\nstatic uint8_t *recvbuf = 0;\n\nint rtp2disk(RtpSession *session, uint32_t ts, int fd) {\n\tint err, havemore = 1;\n\twhile (havemore) {\n\t\terr = rtp_session_recv_with_ts(session, recvbuf, 160, ts, &havemore);\n\t\tif (havemore) printf(\"warning: havemore=%i!\\n\", havemore);\n\t\tif (err > 0) {\n\t\t\trtp_session_set_data(session, (void *)1);\n\t\t\t/* to indicate that (for the application) the stream has started, so we can start\n\t\t\trecording on disk */\n\t\t}\n\t\tif (session->user_data != NULL) {\n\t\t\tsize_t ret = write(fd, recvbuf, err);\n\t\t\tassert(ret == err);\n\t\t}\n\t}\n\treturn 0;\n}\n\nint main(int argc, char *argv[]) {\n\tRtpSession *session[STREAMS_COUNT];\n\tint i;\n\tint filefd[STREAMS_COUNT];\n\tint port;\n\tuint32_t user_ts = 0;\n\tint channels;\n\tSessionSet *set;\n\tchar *filename;\n\n\tif (argc < 4) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\n\tchannels = atoi(argv[3]);\n\tif (channels == 0) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\n\tortp_init();\n\tortp_scheduler_init();\n\n\tport = atoi(argv[2]);\n\trecvbuf = ortp_malloc(160);\n\n\tfor (i = 0; i < channels; i++) {\n\n\t\tsession[i] = rtp_session_new(RTP_SESSION_RECVONLY);\n\t\trtp_session_set_scheduling_mode(session[i], 1);\n\t\trtp_session_set_blocking_mode(session[i], 0);\n\t\trtp_session_set_local_addr(session[i], \"0.0.0.0\", port, port + 1);\n\t\trtp_session_set_payload_type(session[i], 0);\n\t\trtp_session_enable_adaptive_jitter_compensation(session[i], TRUE);\n\t\trtp_session_set_recv_buf_size(session[i], 256);\n\t\tport += 2;\n\t}\n\n\tfilename = ortp_malloc(strlen(argv[1]) + 15);\n\tfor (i = 0; i < channels; i++) {\n\t\tsprintf(filename, \"%s%4.4d.dat\", argv[1], i);\n#ifndef _WIN32\n\t\tfilefd[i] = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);\n#else\n\t\tfilefd[i] = open(filename, _O_BINARY | O_WRONLY | O_CREAT | O_TRUNC);\n#endif\n\t\tif (filefd[i] < 0) ortp_error(\"Could not open %s for writing: %s\", filename, strerror(errno));\n\t}\n\tsignal(SIGINT, stophandler);\n\t/* create a set */\n\tset = session_set_new();\n\twhile (runcond) {\n\t\tint k;\n\n\t\tfor (k = 0; k < channels; k++) {\n\t\t\t/* add the session to the set */\n\t\t\tsession_set_set(set, session[k]);\n\t\t\t// printf(\"session_set_set %d\\n\", k);\n\t\t}\n\t\t/* and then suspend the process by selecting() */\n\t\tk = session_set_select(set, NULL, NULL);\n\t\tif (k == 0) printf(\"warning: session_set_select() is returning 0...\\n\");\n\t\tfor (k = 0; k < channels; k++) {\n\t\t\tif (session_set_is_set(set, session[k])) {\n\t\t\t\trtp2disk(session[k], user_ts, filefd[k]);\n\t\t\t\t// printf(\"session_set_is_set %d\\n\", k);\n\t\t\t} else {\n\t\t\t\t// printf(\"warning: session %i is not set !\\n\",k);\n\t\t\t}\n\t\t}\n\t\tuser_ts += 160;\n\t}\n\tprintf(\"Exiting\\n\");\n\tfor (i = 0; i < channels; i++) {\n\t\tclose(filefd[i]);\n\t\trtp_session_destroy(session[i]);\n\t}\n\tsession_set_destroy(set);\n\tortp_free(filename);\n\tortp_exit();\n\tortp_global_stats_display();\n\tortp_free(recvbuf);\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/mrtpsend.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* this program shows how to send streams in paralel using the SessionSet api\n    and two threads only. */\n\n#include <ortp/ortp.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#ifndef _WIN32\n#include <sys/time.h>\n#include <sys/types.h>\n#endif\n\nint runcond = 1;\n\nvoid stophandler(int signum) {\n\truncond = 0;\n}\n\nstatic char *help = \"usage: mrtpsend\tfilename ip port nstreams [--packet-size size] [--ts-inc value]\\n\";\n\n#define STREAMS_COUNT 1000\n\nint main(int argc, char *argv[]) {\n\tRtpSession *session[STREAMS_COUNT];\n\tunsigned char *buffer;\n\tint packet_size = 160;\n\tint ts_inc = 160;\n\tint i;\n\tFILE *infile;\n\tchar *ssrc;\n\tint port;\n\tuint32_t user_ts = 0;\n\tint channels;\n\tSessionSet *set;\n\n\tif (argc < 5) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\n\tchannels = atoi(argv[4]);\n\tif (channels == 0) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\n\t/* look at command line options */\n\tfor (i = 5; i < argc; i++) {\n\t\tif (strcmp(argv[i], \"--packet-size\") == 0) {\n\t\t\tif (i + 1 < argc) {\n\t\t\t\tpacket_size = atoi(argv[i + 1]);\n\t\t\t} else {\n\t\t\t\tprintf(\"%s\", help);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (packet_size == 0) {\n\t\t\t\tprintf(\"Packet size can't be %s.\\n\", argv[i + 1]);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\ti += 1;\n\n\t\t} else if (strcmp(argv[i], \"--ts-inc\") == 0) {\n\t\t\tif (i + 1 < argc) {\n\t\t\t\tts_inc = atoi(argv[i + 1]);\n\t\t\t} else {\n\t\t\t\tprintf(\"%s\", help);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (ts_inc == 0) {\n\t\t\t\tprintf(\"Timestanp increment can't be %s.\\n\", argv[i + 1]);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\ti += 1;\n\t\t}\n\t}\n\tprintf(\"Timestamp increment will be %i\\n\", ts_inc);\n\tprintf(\"Packet size will be %i\\n\", packet_size);\n\tbuffer = ortp_malloc(packet_size);\n\tortp_init();\n\tortp_scheduler_init();\n\tprintf(\"scheduler initialized\\n\");\n\tssrc = getenv(\"SSRC\");\n\tport = atoi(argv[3]);\n\tfor (i = 0; i < channels; i++) {\n\t\tprintf(\"channel %d\\n\", i);\n\t\tsession[i] = rtp_session_new(RTP_SESSION_SENDONLY);\n\t\trtp_session_set_scheduling_mode(session[i], 1);\n\t\trtp_session_set_blocking_mode(session[i], 0);\n\t\trtp_session_set_remote_addr(session[i], argv[2], port);\n\t\trtp_session_set_payload_type(session[i], 0);\n\t\tif (ssrc != NULL) rtp_session_set_ssrc(session[i], atoi(ssrc));\n\t\tport += 2;\n\t}\n\n#ifndef _WIN32\n\tinfile = fopen(argv[1], \"r\");\n#else\n\tinfile = fopen(argv[1], \"rb\");\n#endif\n\tif (infile == NULL) {\n\t\tperror(\"Cannot open file\");\n\t\treturn -1;\n\t}\n\tprintf(\"open file\\n\");\n\tsignal(SIGINT, stophandler);\n\t/* create a set */\n\tset = session_set_new();\n\twhile (((i = fread(buffer, 1, packet_size, infile)) > 0) && (runcond)) {\n\t\tint k;\n\t\t// ortp_message(\"Sending packet.\");\n\t\tfor (k = 0; k < channels; k++) {\n\t\t\t/* add the session to the set */\n\t\t\tsession_set_set(set, session[k]);\n\t\t}\n\t\t/* and then suspend the process by selecting() */\n\t\tsession_set_select(NULL, set, NULL);\n\t\tfor (k = 0; k < channels; k++) {\n\t\t\t/* this is stupid to do this test, because all session work the same way,\n\t\t\tas the same user_ts is used for all sessions, here. */\n\t\t\tif (session_set_is_set(set, session[k])) {\n\t\t\t\trtp_session_send_with_ts(session[k], buffer, i, user_ts);\n\t\t\t\t// ortp_message(\"packet sended !\");\n\t\t\t}\n\t\t}\n\t\tuser_ts += ts_inc;\n\t}\n\tfclose(infile);\n\tprintf(\"close file\\n\");\n/*sleep a little to wait last packets to be sent */\n#ifndef _WIN32\n\tsleep(1);\n#else\n\tSleep(1);\n#endif\n\tfor (i = 0; i < channels; i++)\n\t\trtp_session_destroy(session[i]);\n\tsession_set_destroy(set);\n\tortp_free(buffer);\n\tortp_exit();\n\tortp_global_stats_display();\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/rtpmemtest.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* this program shows how to receive streams in paralel using the SessionSet api\n    and two threads only. */\n\n#include <assert.h>\n#include <ortp/ortp.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#ifndef _WIN32\n#include <fcntl.h>\n#include <signal.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n#else\n//#include <time.h>\n#endif\n\nint runcond = 1;\n\nvoid stophandler(int signum) {\n\truncond = 0;\n}\n\nstatic char *help = \"usage: mrtprecv\tfile_prefix local_port number_of_streams \\n\"\n                    \"Receives multiples rtp streams on local_port+2*k, k={0..number_of_streams}\\n\";\n\n#define STREAMS_COUNT 1000\n\nint rtp2disk(RtpSession *session, uint32_t ts, int fd) {\n\tunsigned char buffer[160];\n\tint err, havemore = 1;\n\twhile (havemore) {\n\t\terr = rtp_session_recv_with_ts(session, buffer, 160, ts, &havemore);\n\t\tif (err > 0) {\n\t\t\trtp_session_set_data(session, (void *)1);\n\t\t\t/* to indicate that (for the application) the stream has started, so we can start\n\t\t\trecording on disk */\n\t\t}\n\t\tif (session->user_data != NULL) {\n\t\t\tsize_t ret = write(fd, buffer, err);\n\t\t\tassert(ret == err);\n\t\t}\n\t}\n\treturn 0;\n}\n\nint main(int argc, char *argv[]) {\n\tRtpSession *session[STREAMS_COUNT];\n\tint i;\n\tint filefd[STREAMS_COUNT];\n\tint port;\n\tuint32_t user_ts = 0;\n\tint channels;\n\tSessionSet *set;\n\tchar *filename;\n\n\targc = 4;\n\targv[1] = \"/tmp/output\";\n\targv[2] = \"8000\";\n\targv[3] = \"100\";\n\n\tif (argc < 4) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\n\tchannels = atoi(argv[3]);\n\tif (channels == 0) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\n\tortp_init();\n\tortp_scheduler_init();\n\n\tport = atoi(argv[2]);\n\tfor (i = 0; i < channels; i++) {\n\t\tsession[i] = rtp_session_new(RTP_SESSION_RECVONLY);\n\t\trtp_session_set_scheduling_mode(session[i], 1);\n\t\trtp_session_set_blocking_mode(session[i], 0);\n\t\trtp_session_set_local_addr(session[i], \"::\", port, port + 1);\n\t\trtp_session_set_payload_type(session[i], 0);\n\t\trtp_session_set_recv_buf_size(session[i], 256);\n\t\tport += 2;\n\t}\n\n\tfilename = ortp_malloc(strlen(argv[1]) + 8);\n\tfor (i = 0; i < channels; i++) {\n\t\tsprintf(filename, \"%s%4.4d.dat\", argv[1], i);\n\t\tfilefd[i] = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);\n\t\tif (filefd[i] < 0) ortp_error(\"Could not open %s for writing: %s\", filename, strerror(errno));\n\t}\n\tortp_free(filename);\n\tsignal(SIGINT, stophandler);\n\t/* create a set */\n\tset = session_set_new();\n\twhile (runcond) {\n\t\tint k;\n\n\t\tfor (k = 0; k < channels; k++) {\n\t\t\t/* add the session to the set */\n\t\t\tsession_set_set(set, session[k]);\n\t\t\t// printf(\"session[k]->setflags=%i\\n\",session[k]->setflags);\n\t\t}\n\t\t/* and then suspend the process by selecting() */\n\t\tsession_set_select(set, NULL, NULL);\n\t\tfor (k = 0; k < channels; k++) {\n\t\t\tif (session_set_is_set(set, session[k])) {\n\t\t\t\trtp2disk(session[k], user_ts, filefd[k]);\n\t\t\t}\n\t\t}\n\t\tuser_ts += 160;\n\t}\n\tfor (i = 0; i < channels; i++) {\n\t\tclose(filefd[i]);\n\t\trtp_session_destroy(session[i]);\n\t}\n\tsession_set_destroy(set);\n\tortp_global_stats_display();\n\tortp_exit();\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/rtprecv.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <bctoolbox/vfs.h>\n#include <ortp/ortp.h>\n#include <signal.h>\n#include <stdlib.h>\n#ifndef _WIN32\n#include <stdio.h>\n#include <sys/types.h>\n#include <unistd.h>\n#endif\n\nint cond = 1;\n\nvoid stop_handler(int signum) {\n\tcond = 0;\n}\n\nvoid ssrc_cb(RtpSession *session) {\n\tprintf(\"hey, the ssrc has changed !\\n\");\n}\n\nstatic char *help =\n    \"usage: rtprecv  filename loc_port [--format format] [--soundcard] [--noadapt] [--with-jitter <milliseconds>]\\n\";\n\n#define MULAW 0\n#define ALAW 1\n\n#if defined(__hpux) && HAVE_SYS_AUDIO_H\n\n#include <sys/audio.h>\n\nint sound_init(int format) {\n\tint fd;\n\tfd = open(\"/dev/audio\", O_WRONLY);\n\tif (fd < 0) {\n\t\tperror(\"Can't open /dev/audio\");\n\t\treturn -1;\n\t}\n\tioctl(fd, AUDIO_RESET, 0);\n\tioctl(fd, AUDIO_SET_SAMPLE_RATE, 8000);\n\tioctl(fd, AUDIO_SET_CHANNELS, 1);\n\tif (format == MULAW) ioctl(fd, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_ULAW);\n\telse ioctl(fd, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_ALAW);\n\treturn fd;\n}\n#else\nint sound_init(int format) {\n\treturn -1;\n}\n#endif\n\nint main(int argc, char *argv[]) {\n\tRtpSession *session;\n\tunsigned char buffer[160];\n\tint err;\n\tuint32_t ts = 0;\n\tint stream_received = 0;\n\tFILE *outfile;\n\tint local_port;\n\tint have_more;\n\tint i;\n\tint format = 0;\n\tint soundcard = 0;\n\tint sound_fd = 0;\n\tint jittcomp = 40;\n\tbool_t adapt = TRUE;\n\n\t/* init the lib */\n\tif (argc < 3) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\tlocal_port = atoi(argv[2]);\n\tif (local_port <= 0) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\tfor (i = 3; i < argc; i++) {\n\t\tif (strcmp(argv[i], \"--noadapt\") == 0) adapt = FALSE;\n\t\tif (strcmp(argv[i], \"--format\") == 0) {\n\t\t\ti++;\n\t\t\tif (i < argc) {\n\t\t\t\tif (strcmp(argv[i], \"mulaw\") == 0) {\n\t\t\t\t\tformat = MULAW;\n\t\t\t\t} else if (strcmp(argv[i], \"alaw\") == 0) {\n\t\t\t\t\tformat = ALAW;\n\t\t\t\t} else {\n\t\t\t\t\tprintf(\"Unsupported format %s\\n\", argv[i]);\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (strcmp(argv[i], \"--soundcard\") == 0) {\n\t\t\tsoundcard = 1;\n\t\t} else if (strcmp(argv[i], \"--with-jitter\") == 0) {\n\t\t\ti++;\n\t\t\tif (i < argc) {\n\t\t\t\tjittcomp = atoi(argv[i]);\n\t\t\t\tprintf(\"Using a jitter buffer of %i milliseconds.\\n\", jittcomp);\n\t\t\t}\n\t\t}\n\t}\n\n\toutfile = fopen(argv[1], \"wb\");\n\tif (outfile == NULL) {\n\t\tperror(\"Cannot open file for writing\");\n\t\treturn -1;\n\t}\n\n\tif (soundcard) {\n\t\tsound_fd = sound_init(format);\n\t}\n\n\tortp_init();\n\tortp_scheduler_init();\n\tortp_set_log_level_mask(NULL, ORTP_DEBUG | ORTP_MESSAGE | ORTP_WARNING | ORTP_ERROR);\n\tsignal(SIGINT, stop_handler);\n\tsession = rtp_session_new(RTP_SESSION_RECVONLY);\n\trtp_session_set_scheduling_mode(session, 1);\n\trtp_session_set_blocking_mode(session, 1);\n\trtp_session_set_local_addr(session, \"0.0.0.0\", atoi(argv[2]), -1);\n\trtp_session_set_connected_mode(session, TRUE);\n\trtp_session_set_symmetric_rtp(session, TRUE);\n\trtp_session_enable_adaptive_jitter_compensation(session, adapt);\n\trtp_session_set_jitter_compensation(session, jittcomp);\n\trtp_session_set_payload_type(session, 0);\n\trtp_session_signal_connect(session, \"ssrc_changed\", (RtpCallback)ssrc_cb, 0);\n\trtp_session_signal_connect(session, \"ssrc_changed\", (RtpCallback)rtp_session_reset, 0);\n\n\twhile (cond) {\n\t\thave_more = 1;\n\t\twhile (have_more) {\n\t\t\terr = rtp_session_recv_with_ts(session, buffer, 160, ts, &have_more);\n\t\t\tif (err > 0) stream_received = 1;\n\t\t\t/* this is to avoid to write to disk some silence before the first RTP packet is returned*/\n\t\t\tif ((stream_received) && (err > 0)) {\n\t\t\t\tsize_t ret = fwrite(buffer, 1, err, outfile);\n\t\t\t\tif (sound_fd > 0) {\n\t\t\t\t\tret = write(sound_fd, buffer, err);\n\t\t\t\t\tif (ret == -1) {\n\t\t\t\t\t\tfprintf(stderr, \"write to sound card failed (%s)\", strerror(errno));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tts += 160;\n\t\t// ortp_message(\"Receiving packet.\");\n\t}\n\n\trtp_session_destroy(session);\n\tortp_exit();\n\n\tortp_global_stats_display();\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/rtpsend.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <ortp/ortp.h>\n#include <signal.h>\n#include <stdlib.h>\n\n#ifndef _WIN32\n#include <stdio.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#endif\n\nint runcond = 1;\n\nvoid stophandler(int signum) {\n\truncond = 0;\n}\n\nstatic const char *help =\n    \"usage: rtpsend\tfilename dest_ip4addr dest_port [ --with-clockslide <value> ] [ --with-jitter <milliseconds>]\\n\";\n\nint main(int argc, char *argv[]) {\n\tRtpSession *session;\n\tunsigned char buffer[160];\n\tint i;\n\tFILE *infile;\n\tchar *ssrc;\n\tuint32_t user_ts = 0;\n\tint clockslide = 0;\n\tint jitter = 0;\n\tif (argc < 4) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\tfor (i = 4; i < argc; i++) {\n\t\tif (strcmp(argv[i], \"--with-clockslide\") == 0) {\n\t\t\ti++;\n\t\t\tif (i >= argc) {\n\t\t\t\tprintf(\"%s\", help);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tclockslide = atoi(argv[i]);\n\t\t\tortp_message(\"Using clockslide of %i milisecond every 50 packets.\", clockslide);\n\t\t} else if (strcmp(argv[i], \"--with-jitter\") == 0) {\n\t\t\tortp_message(\"Jitter will be added to outgoing stream.\");\n\t\t\ti++;\n\t\t\tif (i >= argc) {\n\t\t\t\tprintf(\"%s\", help);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tjitter = atoi(argv[i]);\n\t\t}\n\t}\n\n\tortp_init();\n\tortp_scheduler_init();\n\tortp_set_log_level_mask(NULL, ORTP_MESSAGE | ORTP_WARNING | ORTP_ERROR);\n\tsession = rtp_session_new(RTP_SESSION_SENDONLY);\n\n\trtp_session_set_scheduling_mode(session, 1);\n\trtp_session_set_blocking_mode(session, 1);\n\trtp_session_set_connected_mode(session, TRUE);\n\trtp_session_set_remote_addr(session, argv[2], atoi(argv[3]));\n\trtp_session_set_payload_type(session, 0);\n\n\tssrc = getenv(\"SSRC\");\n\tif (ssrc != NULL) {\n\t\tprintf(\"using SSRC=%i.\\n\", atoi(ssrc));\n\t\trtp_session_set_ssrc(session, atoi(ssrc));\n\t}\n\n#ifndef _WIN32\n\tinfile = fopen(argv[1], \"r\");\n#else\n\tinfile = fopen(argv[1], \"rb\");\n#endif\n\n\tif (infile == NULL) {\n\t\tperror(\"Cannot open file\");\n\t\treturn -1;\n\t}\n\n\tsignal(SIGINT, stophandler);\n\twhile (((i = fread(buffer, 1, 160, infile)) > 0) && (runcond)) {\n\t\trtp_session_send_with_ts(session, buffer, i, user_ts);\n\t\tuser_ts += 160;\n\t\tif (clockslide != 0 && user_ts % (160 * 50) == 0) {\n\t\t\tortp_message(\"Clock sliding of %i miliseconds now\", clockslide);\n\t\t\trtp_session_make_time_distorsion(session, clockslide);\n\t\t}\n\t\t/*this will simulate a burst of late packets */\n\t\tif (jitter && (user_ts % (8000) == 0)) {\n\t\t\tortp_message(\"Simulating late packets now (%i milliseconds)\", jitter);\n\t\t\tbctbx_sleep_ms(jitter);\n\t\t}\n\t}\n\n\tfclose(infile);\n\trtp_session_destroy(session);\n\tortp_exit();\n\tortp_global_stats_display();\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/rtpsend_stupid.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <ortp/ortp.h>\n#include <signal.h>\n#include <stdlib.h>\n\n#ifndef _WIN32\n#include <stdio.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#endif\n/*defined in library, but not declared in public headers (this method is only useful for tests)*/\nORTP_PUBLIC extern int\n__rtp_session_sendm_with_ts(RtpSession *session, mblk_t *packet, uint32_t packet_ts, uint32_t send_ts);\n\nint runcond = 1;\n\nvoid stophandler(int signum) {\n\truncond = 0;\n}\n\nstatic char *help =\n    \"usage: rtpsend\tfilename dest_ip4addr dest_port [ --with-clockslide <value> ] [ --with-ptime <milliseconds>]\\n\";\n\nint main(int argc, char *argv[]) {\n\tRtpSession *session;\n\tunsigned char buffer[160];\n\tint i;\n\tFILE *infile;\n\tchar *ssrc;\n\tuint32_t packet_ts = 0, send_ts = 0;\n\tuint32_t send_ts_inc = 160;\n\tint clockslide = 0;\n\tint jitter = 0;\n\tif (argc < 4) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\tfor (i = 4; i < argc; i++) {\n\t\tif (strcmp(argv[i], \"--with-clockslide\") == 0) {\n\t\t\ti++;\n\t\t\tif (i >= argc) {\n\t\t\t\tprintf(\"%s\", help);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tclockslide = atoi(argv[i]);\n\t\t\tortp_message(\"Using clockslide of %i milisecond every 50 packets.\", clockslide);\n\t\t} else if (strcmp(argv[i], \"--with-ptime\") == 0) {\n\t\t\tortp_message(\"Ptime related jitter will be added to outgoing stream.\");\n\t\t\ti++;\n\t\t\tif (i >= argc) {\n\t\t\t\tprintf(\"%s\", help);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tjitter = atoi(argv[i]);\n\t\t\tsend_ts_inc = jitter * 8;\n\t\t}\n\t}\n\n\tortp_init();\n\tortp_scheduler_init();\n\tortp_set_log_level_mask(NULL, ORTP_MESSAGE | ORTP_WARNING | ORTP_ERROR);\n\tsession = rtp_session_new(RTP_SESSION_SENDONLY);\n\n\trtp_session_set_scheduling_mode(session, 1);\n\trtp_session_set_blocking_mode(session, 1);\n\trtp_session_set_connected_mode(session, TRUE);\n\trtp_session_set_remote_addr(session, argv[2], atoi(argv[3]));\n\trtp_session_set_payload_type(session, 0);\n\n\tssrc = getenv(\"SSRC\");\n\tif (ssrc != NULL) {\n\t\tprintf(\"using SSRC=%i.\\n\", atoi(ssrc));\n\t\trtp_session_set_ssrc(session, atoi(ssrc));\n\t}\n\n#ifndef _WIN32\n\tinfile = fopen(argv[1], \"r\");\n#else\n\tinfile = fopen(argv[1], \"rb\");\n#endif\n\n\tif (infile == NULL) {\n\t\tperror(\"Cannot open file\");\n\t\treturn -1;\n\t}\n\n\tsignal(SIGINT, stophandler);\n\twhile (((i = fread(buffer, 1, 160, infile)) > 0) && (runcond)) {\n\t\tmblk_t *m = rtp_session_create_packet(session, RTP_FIXED_HEADER_SIZE, buffer, i);\n\t\t__rtp_session_sendm_with_ts(session, m, packet_ts, send_ts);\n\t\tpacket_ts += 160;\n\t\tif ((send_ts + send_ts_inc) <= packet_ts) {\n\t\t\tsend_ts += send_ts_inc;\n\t\t}\n\t\tif (clockslide != 0 && send_ts % (160 * 50) == 0) {\n\t\t\tortp_message(\"Clock sliding of %i miliseconds now\", clockslide);\n\t\t\trtp_session_make_time_distorsion(session, clockslide);\n\t\t}\n\t}\n\n\tfclose(infile);\n\trtp_session_destroy(session);\n\tortp_exit();\n\tortp_global_stats_display();\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/test_timer.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"../rtptimer.h\"\n#include <stdio.h>\n\nint main(int argc, char *argv[]) {\n\tRtpTimer *timer = &posix_timer;\n\tint i;\n\tstruct timeval interval;\n\n\tinterval.tv_sec = 0;\n\tinterval.tv_usec = 500000;\n\n\trtp_timer_set_interval(timer, &interval);\n\n\ttimer->timer_init();\n\tfor (i = 0; i < 10; i++) {\n\t\tprintf(\"doing something...\\n\");\n\t\ttimer->timer_do();\n\t}\n\ttimer->timer_uninit();\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/tevmrtprecv.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* this program shows how to receive streams in paralel using the SessionSet api\n    and two threads only. */\n\n#include <assert.h>\n#include <ortp/ortp.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#ifndef _WIN32\n#include <fcntl.h>\n#include <signal.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#else\n//#include <time.h>\n#endif\n\n#include <ortp/telephonyevents.h>\n\nint runcond = 1;\n\nvoid stophandler(int signum) {\n\truncond = 0;\n}\n\nstatic int dtmf_tab[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '#', 'A', 'B', 'C', 'D'};\n\nstatic int *p_channel_id;\n\nint dtmf_count = 0;\n\nstatic char *help =\n    \"usage: tevmrtprecv\tfile_prefix local_port number_of_streams \\n\"\n    \"Receives multiples rtp streams with telephone events on local_port+2*k, k={0..number_of_streams}\\n\";\n\n#define STREAMS_COUNT 1000\n\nvoid recv_tev_cb(RtpSession *session, unsigned long type, unsigned long dummy, void *user_data) {\n\t// printf(\"Receiving telephony event:%i\\n\",type);\n\tif (type < 16) printf(\"This is dtmf %c on channel %d\\n\", dtmf_tab[type], *(int *)user_data);\n\tdtmf_count++;\n}\n\nint rtp2disk(RtpSession *session, uint32_t ts, int fd) {\n\tunsigned char buffer[160];\n\tint err, havemore = 1;\n\twhile (havemore) {\n\t\terr = rtp_session_recv_with_ts(session, buffer, 160, ts, &havemore);\n\t\tif (err > 0) {\n\t\t\trtp_session_set_data(session, (void *)1);\n\t\t\t/* to indicate that (for the application) the stream has started, so we can start\n\t\t\trecording on disk */\n\t\t}\n\t\tif (session->user_data != NULL) {\n\t\t\tsize_t ret = write(fd, buffer, err);\n\t\t\tassert(ret == err);\n\t\t}\n\t}\n\treturn 0;\n}\n\nint main(int argc, char *argv[]) {\n\tRtpSession *session[STREAMS_COUNT];\n\tint i;\n\tint filefd[STREAMS_COUNT];\n\tint port;\n\tuint32_t user_ts = 0;\n\tint channels;\n\tSessionSet *set;\n\tchar *filename;\n\n\tif (argc < 4) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\n\tchannels = atoi(argv[3]);\n\tif (channels == 0) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\n\tortp_init();\n\tortp_scheduler_init();\n\n\t/* set the telephony event payload type to 96 in the av profile.*/\n\trtp_profile_set_payload(&av_profile, 96, &payload_type_telephone_event);\n\n\tport = atoi(argv[2]);\n\tp_channel_id = (int *)ortp_malloc(channels * sizeof(int));\n\tfor (i = 0; i < channels; i++) {\n\t\tsession[i] = rtp_session_new(RTP_SESSION_RECVONLY);\n\t\trtp_session_set_scheduling_mode(session[i], 1);\n\t\trtp_session_set_blocking_mode(session[i], 0);\n\n\t\trtp_session_set_local_addr(session[i], \"0.0.0.0\", port, port + 1);\n\t\trtp_session_set_recv_payload_type(session[i], 0);\n\t\trtp_session_set_recv_buf_size(session[i], 256);\n\n\t\tp_channel_id[i] = i;\n\t\t/* register for telephony events */\n\t\trtp_session_signal_connect(session[i], \"telephone-event\", (RtpCallback)recv_tev_cb, &p_channel_id[i]);\n\n\t\tport += 2;\n\t}\n\n\tfilename = ortp_malloc(strlen(argv[1]) + 8);\n\tfor (i = 0; i < channels; i++) {\n\t\tsprintf(filename, \"%s%4.4d.dat\", argv[1], i);\n#ifndef _WIN32\n\t\tfilefd[i] = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);\n#else\n\t\tfilefd[i] = open(filename, _O_BINARY | O_WRONLY | O_CREAT | O_TRUNC);\n#endif\n\t\tif (filefd[i] < 0) ortp_error(\"Could not open %s for writing: %s\", filename, strerror(errno));\n\t}\n\tsignal(SIGINT, stophandler);\n\t/* create a set */\n\tset = session_set_new();\n\twhile (runcond) {\n\t\tint k;\n\n\t\tfor (k = 0; k < channels; k++) {\n\t\t\t/* add the session to the set */\n\t\t\tsession_set_set(set, session[k]);\n\t\t}\n\t\t/* and then suspend the process by selecting() */\n\t\tsession_set_select(set, NULL, NULL);\n\t\tfor (k = 0; k < channels; k++) {\n\t\t\tif (session_set_is_set(set, session[k])) {\n\t\t\t\trtp2disk(session[k], user_ts, filefd[k]);\n\t\t\t}\n\t\t}\n\t\tuser_ts += 160;\n\t}\n\tfor (i = 0; i < channels; i++) {\n\t\tclose(filefd[i]);\n\t\trtp_session_destroy(session[i]);\n\t}\n\tsession_set_destroy(set);\n\tortp_free(p_channel_id);\n\tortp_free(filename);\n\tortp_exit();\n\tortp_global_stats_display();\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/tevrtprecv.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <assert.h>\n#include <ortp/ortp.h>\n#include <ortp/telephonyevents.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#ifndef _WIN32\n#include <sys/time.h>\n#else\n#include <time.h>\n#endif\n#include <stdio.h>\n\nint runcond = 1;\n\nstatic int dtmf_tab[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '#', 'A', 'B', 'C', 'D'};\n\nvoid stophandler(int signum) {\n\truncond = 0;\n}\n\nstatic char *help = \"usage: test_tevrecv\tfilename loc_port\\n\";\n\nint dtmf_count = 0;\n\nvoid recv_tev_cb(RtpSession *session, unsigned long type, unsigned long dummy, void *user_data) {\n\tprintf(\"Receiving telephony event:%lu\\n\", type);\n\tif (type < 16) printf(\"This is dtmf %c\\n\", dtmf_tab[type]);\n\tdtmf_count++;\n}\n\nint main(int argc, char *argv[]) {\n\tRtpSession *session;\n\tunsigned char buffer[160];\n\tint err;\n\tFILE *outfile;\n\tuint32_t ts = 0;\n\tint have_more;\n\n\tif (argc < 3) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\n\tortp_init();\n\tortp_scheduler_init();\n\n\t/* set the telephony event payload type to 96 in the av profile.*/\n\trtp_profile_set_payload(&av_profile, 96, &payload_type_telephone_event);\n\n\tsession = rtp_session_new(RTP_SESSION_RECVONLY);\n\n\trtp_session_set_scheduling_mode(session, 1);\n\trtp_session_set_blocking_mode(session, 1);\n\trtp_session_set_local_addr(session, \"0.0.0.0\", atoi(argv[2]), -1);\n\trtp_session_set_payload_type(session, 0);\n\n\t/* register for telephony events */\n\trtp_session_signal_connect(session, \"telephone-event\", (RtpCallback)recv_tev_cb, 0);\n\n\toutfile = fopen(argv[1], \"wb\");\n\tif (outfile == NULL) {\n\t\tperror(\"Cannot open file\");\n\t\treturn -1;\n\t}\n\tsignal(SIGINT, stophandler);\n\twhile (runcond) {\n\t\thave_more = 1;\n\t\twhile (have_more) {\n\t\t\terr = rtp_session_recv_with_ts(session, buffer, 160, ts, &have_more);\n\t\t\tif (err > 0) {\n\t\t\t\tassert(fwrite(buffer, 1, err, outfile) == err);\n\t\t\t}\n\t\t}\n\t\tts += 160;\n\t\t// ortp_message(\"Receiving packet.\");\n\t}\n\tfclose(outfile);\n\trtp_session_destroy(session);\n\tortp_exit();\n\tortp_global_stats_display();\n\tprintf(\"Total dtmf events received: %i\\n\", dtmf_count);\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/tevrtpsend.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <ortp/ortp.h>\n#include <ortp/telephonyevents.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#ifndef _WIN32\n#include <sys/time.h>\n#else\n#include <time.h>\n#endif\n#include <stdio.h>\n\nint runcond = 1;\n\nvoid stophandler(int signum) {\n\truncond = 0;\n}\n\nstatic char *help = \"usage: test_tevsend\tfilename dest_ip4addr dest_port\\n\";\n\nint main(int argc, char *argv[]) {\n\tRtpSession *session;\n\tunsigned char buffer[160];\n\tint i;\n\tFILE *infile;\n\tchar *ssrc;\n\tuint32_t user_ts = 0;\n\tint tel = 0;\n\n\tif (argc < 4) {\n\t\tprintf(\"%s\", help);\n\t\treturn -1;\n\t}\n\n\tortp_init();\n\tortp_scheduler_init();\n\n\t/* set the telephony event payload type to 96 in the av profile.*/\n\trtp_profile_set_payload(&av_profile, 96, &payload_type_telephone_event);\n\n\tsession = rtp_session_new(RTP_SESSION_SENDONLY);\n\n\trtp_session_set_scheduling_mode(session, 1);\n\trtp_session_set_blocking_mode(session, 1);\n\trtp_session_set_remote_addr(session, argv[2], atoi(argv[3]));\n\trtp_session_set_send_payload_type(session, 0);\n\n\tssrc = getenv(\"SSRC\");\n\tif (ssrc != NULL) {\n\t\tprintf(\"using SSRC=%i.\\n\", atoi(ssrc));\n\t\trtp_session_set_ssrc(session, atoi(ssrc));\n\t}\n\n\tinfile = fopen(argv[1], \"rb\");\n\tif (infile == NULL) {\n\t\tperror(\"Cannot open file\");\n\t\treturn -1;\n\t}\n\tsignal(SIGINT, stophandler);\n\twhile (((i = fread(buffer, 1, 160, infile)) > 0) && (runcond)) {\n\t\t// ortp_message(\"Sending packet.\");\n\t\trtp_session_send_with_ts(session, buffer, i, user_ts);\n\t\tuser_ts += 160;\n\t\ttel++;\n\t\tif (tel == 50) {\n\t\t\ttel = 0;\n\t\t\tortp_message(\"Sending telephony event packet.\");\n\t\t\trtp_session_send_dtmf(session, '*', user_ts);\n\t\t\tuser_ts += 160 + 160 + 160; /* the duration of the dtmf */\n\t\t}\n\t}\n\tfclose(infile);\n\trtp_session_destroy(session);\n\tortp_exit();\n\tortp_global_stats_display();\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/win_receiver/.gitignore",
    "content": "Makefile.in\nMakefile\n"
  },
  {
    "path": "src/tests/win_receiver/Makefile.am",
    "content": "EXTRA_DIST=RTPReceiver.cpp RTPReceiver.vcproj\n\n"
  },
  {
    "path": "src/tests/win_receiver/RTPReceiver.cpp",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n#include <fcntl.h>\n#include <ortp/ortp.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define STREAMS_COUNT 1000\n\nBOOL m_bExit = FALSE;\n\nstatic char *help = \"usage: mrtprecv\tfile_prefix local_port number_of_streams \\n\"\n                    \"Receives multiples rtp streams on local_port+2*k, k={0..number_of_streams}\\n\";\n\nvoid ProductVersion() {\n\tchar strBuffer[255];\n\n\tprintf(\"====================================\\n\");\n\tprintf(\"Author  : Simon Morlat             =\\n\");\n\tprintf(\"Porting : Yann STEPHAN             =\\n\");\n\tprintf(\"====================================\\n\");\n\n\tmemset(&strBuffer, 0x0, sizeof(strBuffer));\n\n\tsprintf((char *)&strBuffer, \"= RTPReceiver V1.0   - Date : %s - %s\\n\", __DATE__, __TIME__);\n\tprintf(strBuffer);\n\n\tprintf(\"====================================\\n\");\n}\n\nBOOL ctrlHandlerFunction(DWORD fdwCtrlType) {\n\tswitch (fdwCtrlType) {\n\t\t// Handle the CTRL+C signal.\n\t\t// CTRL+CLOSE: confirm that the user wants to exit.\n\t\tcase CTRL_C_EVENT:\n\t\tcase CTRL_CLOSE_EVENT:\n\t\tcase CTRL_BREAK_EVENT:\n\t\tcase CTRL_LOGOFF_EVENT:\n\t\tcase CTRL_SHUTDOWN_EVENT:\n\t\t\tm_bExit = TRUE;\n\t\t\treturn TRUE;\n\n\t\tdefault:\n\t\t\treturn FALSE;\n\t}\n}\n\nint rtp2disk(RtpSession *session, uint32_t ts, FILE *fd) {\n\tchar buffer[160];\n\tint err, havemore = 1;\n\n\twhile (havemore) {\n\t\terr = rtp_session_recv_with_ts(session, buffer, 160, ts, &havemore);\n\n\t\tif (havemore) printf(\"==> Warning: havemore=1!\\n\");\n\n\t\tif (err > 0) {\n\t\t\trtp_session_set_data(session, (void *)1);\n\t\t\t/* to indicate that (for the application) the stream has started, so we can start\n\t\t\trecording on disk */\n\t\t}\n\n\t\tif (session->user_data != NULL) {\n\t\t\tfwrite(&buffer, 1, 160, fd);\n\t\t}\n\t}\n\treturn 0;\n}\n\nint GetSystemInformation() {\n\tSYSTEM_INFO SystemInfo;\n\n\tGetSystemInfo(&SystemInfo);\n\n\treturn SystemInfo.dwNumberOfProcessors;\n}\n\nint __cdecl main(int argc, char *argv[]) {\n\tRtpSession *session[STREAMS_COUNT];\n\tFILE *filefd[STREAMS_COUNT];\n\tSessionSet *set;\n\n\tuint32_t user_ts = 0;\n\n\tint port = 0;\n\tint channels = 0;\n\tint i = 0;\n\tint nCPUCount = 0;\n\tint nSchedulerCPU = 2;\n\n\tchar strFilename[MAX_PATH];\n\n\tProductVersion();\n\n\tif (argc < 4) {\n\t\tprintf(help);\n\t\treturn -1;\n\t}\n\n\tchannels = atoi(argv[3]);\n\tif (channels == 0) {\n\t\tprintf(help);\n\t\treturn -1;\n\t}\n\n\t// Now it's time to use the power of multiple CPUs\n\tnCPUCount = GetSystemInformation();\n\n\tprintf(\"==> # of CPU detected : %d\\n\", nCPUCount);\n\n\tortp_init();\n\tortp_scheduler_init();\n\n\tif (nCPUCount > 1) {\n\t\tif (nCPUCount > 2) {\n\t\t\tnSchedulerCPU = 3;\n\t\t}\n\n\t\t/*\t\tif (ortp_bind_scheduler_to_cpu(nSchedulerCPU) != -1)\n\t\t        {\n\t\t            printf(\"==> Scheduler has been binded to CPU %d\\n\", nSchedulerCPU);\n\t\t        }\n\t\t        else\n\t\t        {\n\t\t            printf(\"==> Scheduler still binded to CPU 1\\n\");\n\t\t        }\n\t\t*/\n\t}\n\n\tport = atoi(argv[2]);\n\n\tfor (i = 0; i < channels; i++) {\n\t\tsession[i] = rtp_session_new(RTP_SESSION_RECVONLY);\n\t\trtp_session_set_scheduling_mode(session[i], 1);\n\t\trtp_session_set_blocking_mode(session[i], 0);\n\t\trtp_session_set_local_addr(session[i], \"0.0.0.0\", port, port + 1);\n\t\trtp_session_set_send_payload_type(session[i], 0);\n\t\trtp_session_enable_adaptive_jitter_compensation(session[i], TRUE);\n\t\trtp_session_set_recv_buf_size(session[i], 256);\n\t\tport += 2;\n\t}\n\n\tmemset(strFilename, 0x0, sizeof(strFilename));\n\n\tfor (i = 0; i < channels; i++) {\n\t\tsprintf(strFilename, \"%s%4.4d.dat\", argv[1], i);\n\n\t\tfilefd[i] = fopen(strFilename, \"wb\");\n\n\t\tif (filefd[i] < 0) {\n\t\t\tprintf(\"Could not open %s for writing: %s\", strFilename, strerror(errno));\n\t\t}\n\t}\n\n\t// =============== INSTALL THE CONTROL HANDLER ===============\n\tif (SetConsoleCtrlHandler((PHANDLER_ROUTINE)ctrlHandlerFunction, TRUE) == 0) {\n\t\tprintf(\"==> Cannot handle the CTRL-C...\\n\");\n\t}\n\n\t/* create a set */\n\tset = session_set_new();\n\tprintf(\"==> RTP Receiver started\\n\");\n\n\twhile (m_bExit == FALSE) {\n\t\tint k;\n\n\t\tfor (k = 0; k < channels; k++) {\n\t\t\t/* add the session to the set */\n\t\t\tsession_set_set(set, session[k]);\n\t\t\t// printf(\"session_set_set %d\\n\", k);\n\t\t}\n\t\t/* and then suspend the process by selecting() */\n\t\tk = session_set_select(set, NULL, NULL);\n\t\t// printf(\"session_set_select\\n\");\n\t\tif (k == 0) {\n\t\t\tprintf(\"==> Warning: session_set_select() is returning 0...\\n\");\n\t\t}\n\n\t\tfor (k = 0; k < channels; k++) {\n\t\t\tif (session_set_is_set(set, session[k])) {\n\t\t\t\trtp2disk(session[k], user_ts, filefd[k]);\n\t\t\t\t// printf(\"==> Session_set_is_set %d\\n\", k);\n\t\t\t} else {\n\t\t\t\t// printf(\"warning: session %i is not set !\\n\",k);\n\t\t\t}\n\t\t}\n\t\tuser_ts += 160;\n\t}\n\n\tprintf(\"==> Exiting\\n\");\n\n\tfor (i = 0; i < channels; i++) {\n\t\tfclose(filefd[i]);\n\t\trtp_session_destroy(session[i]);\n\t}\n\tsession_set_destroy(set);\n\n\tortp_exit();\n\n\tortp_global_stats_display();\n\n\tprintf(\"Waiting for exit : \");\n\n\tfor (i = 0; i < 4 * 5; i++) {\n\t\tprintf(\".\");\n\t\tSleep(250);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/win_receiver/RTPReceiver.vcproj",
    "content": "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\n<VisualStudioProject\n\tProjectType=\"Visual C++\"\n\tVersion=\"8.00\"\n\tName=\"RTPReceiver\"\n\tProjectGUID=\"{14A51171-516C-4D50-A88C-953B9B09F34F}\"\n\tRootNamespace=\"HPRTP_DIRECTX_oRTPReceiver\"\n\tKeyword=\"Win32Proj\"\n\t>\n\t<Platforms>\n\t\t<Platform\n\t\t\tName=\"Win32\"\n\t\t/>\n\t</Platforms>\n\t<ToolFiles>\n\t</ToolFiles>\n\t<Configurations>\n\t\t<Configuration\n\t\t\tName=\"Debug|Win32\"\n\t\t\tOutputDirectory=\"Debug\"\n\t\t\tIntermediateDirectory=\"Debug\"\n\t\t\tConfigurationType=\"1\"\n\t\t\tInheritedPropertySheets=\"$(VCInstallDir)VCProjectDefaults\\UpgradeFromVC71.vsprops\"\n\t\t\tUseOfMFC=\"2\"\n\t\t\tCharacterSet=\"0\"\n\t\t\t>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreBuildEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCustomBuildTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCMIDLTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\tOptimization=\"0\"\n\t\t\t\tAdditionalIncludeDirectories=\"&quot;$(SolutionDir)include&quot;;&quot;$(SolutionDir)include\\ortp&quot;;&quot;$(SolutionDir)src&quot;;&quot;$(SolutionDir)build\\win32native\\include&quot;\"\n\t\t\t\tPreprocessorDefinitions=\"WIN32;_DEBUG;_CONSOLE;WINDOW_NATIVE;_CRT_SECURE_NO_DEPRECATE\"\n\t\t\t\tMinimalRebuild=\"true\"\n\t\t\t\tBasicRuntimeChecks=\"3\"\n\t\t\t\tRuntimeLibrary=\"3\"\n\t\t\t\tUsePrecompiledHeader=\"0\"\n\t\t\t\tWarningLevel=\"3\"\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\n\t\t\t\tDebugInformationFormat=\"4\"\n\t\t\t\tCallingConvention=\"0\"\n\t\t\t\tCompileAs=\"2\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreLinkEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCLinkerTool\"\n\t\t\t\tAdditionalDependencies=\"oRTP.lib Ws2_32.lib Winmm.lib\"\n\t\t\t\tOutputFile=\"$(SolutionDir)Final/Exe/Debug/RTPReceiver.exe\"\n\t\t\t\tLinkIncremental=\"2\"\n\t\t\t\tAdditionalLibraryDirectories=\"$(SolutionDir)Final/Lib/Debug\"\n\t\t\t\tIgnoreAllDefaultLibraries=\"false\"\n\t\t\t\tIgnoreDefaultLibraryNames=\"libcmtd\"\n\t\t\t\tGenerateDebugInformation=\"true\"\n\t\t\t\tProgramDatabaseFile=\"$(OutDir)/RTPReceiver.pdb\"\n\t\t\t\tSubSystem=\"1\"\n\t\t\t\tTargetMachine=\"1\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCALinkTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManifestTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXDCMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCBscMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCFxCopTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCAppVerifierTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebDeploymentTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPostBuildEventTool\"\n\t\t\t/>\n\t\t</Configuration>\n\t\t<Configuration\n\t\t\tName=\"Release|Win32\"\n\t\t\tOutputDirectory=\"Release\"\n\t\t\tIntermediateDirectory=\"Release\"\n\t\t\tConfigurationType=\"1\"\n\t\t\tInheritedPropertySheets=\"$(VCInstallDir)VCProjectDefaults\\UpgradeFromVC71.vsprops\"\n\t\t\tCharacterSet=\"0\"\n\t\t\tWholeProgramOptimization=\"1\"\n\t\t\t>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreBuildEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCustomBuildTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCMIDLTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\tAdditionalIncludeDirectories=\"&quot;$(SolutionDir)include&quot;;&quot;$(SolutionDir)include\\ortp&quot;;&quot;$(SolutionDir)src&quot;;&quot;$(SolutionDir)build\\win32native\\include&quot;\"\n\t\t\t\tPreprocessorDefinitions=\"WIN32;NDEBUG;_CONSOLE;WINDOW_NATIVE;_CRT_SECURE_NO_DEPRECATE\"\n\t\t\t\tRuntimeLibrary=\"0\"\n\t\t\t\tUsePrecompiledHeader=\"0\"\n\t\t\t\tWarningLevel=\"3\"\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\n\t\t\t\tDebugInformationFormat=\"0\"\n\t\t\t\tCallingConvention=\"0\"\n\t\t\t\tCompileAs=\"2\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreLinkEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCLinkerTool\"\n\t\t\t\tAdditionalDependencies=\"oRTP.lib Ws2_32.lib Winmm.lib\"\n\t\t\t\tOutputFile=\"$(SolutionDir)Final/Exe/Release/RTPReceiver.exe\"\n\t\t\t\tLinkIncremental=\"1\"\n\t\t\t\tAdditionalLibraryDirectories=\"$(SolutionDir)Final/Lib/Release\"\n\t\t\t\tIgnoreAllDefaultLibraries=\"false\"\n\t\t\t\tGenerateDebugInformation=\"false\"\n\t\t\t\tSubSystem=\"1\"\n\t\t\t\tOptimizeReferences=\"2\"\n\t\t\t\tEnableCOMDATFolding=\"2\"\n\t\t\t\tTargetMachine=\"1\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCALinkTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManifestTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXDCMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCBscMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCFxCopTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCAppVerifierTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebDeploymentTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPostBuildEventTool\"\n\t\t\t/>\n\t\t</Configuration>\n\t</Configurations>\n\t<References>\n\t</References>\n\t<Files>\n\t\t<Filter\n\t\t\tName=\"Source Files\"\n\t\t\tFilter=\"cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx\"\n\t\t\tUniqueIdentifier=\"{4FC737F1-C7A5-4376-A066-2A32D752A2FF}\"\n\t\t\t>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\RTPReceiver.cpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t</Filter>\n\t\t<Filter\n\t\t\tName=\"Header Files\"\n\t\t\tFilter=\"h;hpp;hxx;hm;inl;inc;xsd\"\n\t\t\tUniqueIdentifier=\"{93995380-89BD-4b04-88EB-625FBE52EBFB}\"\n\t\t\t>\n\t\t</Filter>\n\t\t<Filter\n\t\t\tName=\"Resource Files\"\n\t\t\tFilter=\"rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx\"\n\t\t\tUniqueIdentifier=\"{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}\"\n\t\t\t>\n\t\t</Filter>\n\t</Files>\n\t<Globals>\n\t</Globals>\n</VisualStudioProject>\n"
  },
  {
    "path": "src/tests/win_sender/.gitignore",
    "content": "Makefile.in\nMakefile\n"
  },
  {
    "path": "src/tests/win_sender/Makefile.am",
    "content": "EXTRA_DIST=RTPSender.cpp RTPSender.vcproj\n\n"
  },
  {
    "path": "src/tests/win_sender/RTPSender.cpp",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n#include <ortp/ortp.h>\n#include <string.h>\n\n#define STREAMS_COUNT 1000\n\nenum {\n\tEVENT_STOP,\n\tEVENT_RTP,\n\tEVENT_COUNT //  Always last\n};\n\nRtpSession *m_Session[STREAMS_COUNT];\n\nint m_nPacket_Size = 160;\nint m_nTimestamp_Inc = 160;\n\nchar *m_pBuffer = NULL;\nchar *m_SSRC = NULL;\n\nint m_nChannels = 0;\nint m_nPort = 0;\n\nHANDLE m_hEvents[EVENT_COUNT];\n\nBOOL m_bExit = FALSE;\n\nstatic char *help = \"usage: mrtpsend\tfilename ip port nstreams [--packet-size size] [--ts-inc value]\\n\";\n\nBOOL ctrlHandlerFunction(DWORD fdwCtrlType) {\n\tswitch (fdwCtrlType) {\n\t\t// Handle the CTRL+C signal.\n\t\t// CTRL+CLOSE: confirm that the user wants to exit.\n\t\tcase CTRL_C_EVENT:\n\t\tcase CTRL_CLOSE_EVENT:\n\t\tcase CTRL_BREAK_EVENT:\n\t\tcase CTRL_LOGOFF_EVENT:\n\t\tcase CTRL_SHUTDOWN_EVENT:\n\t\t\tm_bExit = TRUE;\n\t\t\tSetEvent(m_hEvents[EVENT_STOP]);\n\t\t\treturn TRUE;\n\n\t\tdefault:\n\t\t\treturn FALSE;\n\t}\n}\n\nint GetCommandArguments(int argc, char *argv[]) {\n\tint nCounter;\n\n\t// Check the number of arguments\n\tif (argc < 5) {\n\t\tprintf(help);\n\t\treturn -1;\n\t}\n\n\tm_nChannels = atoi(argv[4]);\n\n\t// Get the number of channels\n\tif (m_nChannels == 0) {\n\t\tprintf(help);\n\t\treturn -1;\n\t}\n\n\t/* look at command line options */\n\tfor (nCounter = 5; nCounter < argc; nCounter++) {\n\t\tif (strcmp(argv[nCounter], \"--packet-size\") == 0) {\n\t\t\tif (nCounter + 1 < argc) {\n\t\t\t\tm_nPacket_Size = atoi(argv[nCounter + 1]);\n\t\t\t} else {\n\t\t\t\tprintf(help);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (m_nPacket_Size == 0) {\n\t\t\t\tprintf(\"Packet size can't be %s.\\n\", argv[nCounter + 1]);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tnCounter += 1;\n\n\t\t} else if (strcmp(argv[nCounter], \"--ts-inc\") == 0) {\n\t\t\tif (nCounter + 1 < argc) {\n\t\t\t\tm_nTimestamp_Inc = atoi(argv[nCounter + 1]);\n\t\t\t} else {\n\t\t\t\tprintf(help);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (m_nTimestamp_Inc == 0) {\n\t\t\t\tprintf(\"Timestanp increment can't be %s.\\n\", argv[nCounter + 1]);\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tnCounter += 1;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nvoid ProductVersion() {\n\tchar strBuffer[255];\n\n\tprintf(\"====================================\\n\");\n\tprintf(\"Author  : Simon Morlat             =\\n\");\n\tprintf(\"Porting : Yann STEPHAN             =\\n\");\n\tprintf(\"====================================\\n\");\n\n\tmemset(&strBuffer, 0x0, sizeof(strBuffer));\n\n\tsprintf((char *)&strBuffer, \"= RTPSender V1.0   - Date : %s - %s\\n\", __DATE__, __TIME__);\n\tprintf(strBuffer);\n\n\tprintf(\"====================================\\n\");\n}\n\nint __cdecl main(int argc, char *argv[]) {\n\tFILE *infile = NULL;\n\tSessionSet *pSessionSet = NULL;\n\tint nCounter = 0;\n\tUINT32 m_nUser_Timestamp = 0;\n\n\tProductVersion();\n\n\tif (GetCommandArguments(argc, argv) != 0) {\n\t\tprintf(\"==> Sorry dude...\\n\");\n\t\tSleep(1000);\n\t\treturn -1;\n\t}\n\n\tprintf(\"==> Starting the RTP Sender test\\n\");\n\n\t// =============== INSTALL THE CONTROL HANDLER ===============\n\tif (SetConsoleCtrlHandler((PHANDLER_ROUTINE)ctrlHandlerFunction, TRUE) == 0) {\n\t\tprintf(\"==> Cannot handle the CTRL-C...\\n\");\n\t}\n\n\tprintf(\"==> Timestamp increment will be %i\\n\", m_nTimestamp_Inc);\n\tprintf(\"==> Packet size will be %i\\n\", m_nPacket_Size);\n\n\tm_pBuffer = (char *)ortp_malloc(m_nPacket_Size);\n\n\tortp_init();\n\tortp_scheduler_init();\n\tprintf(\"==> Scheduler initialized\\n\");\n\n\tm_SSRC = getenv(\"SSRC\");\n\tm_nPort = atoi(argv[3]);\n\n\tfor (nCounter = 0; nCounter < m_nChannels; nCounter++) {\n\t\t// printf(\"==> Channel [#%d]\\n\", nCounter);\n\n\t\tm_Session[nCounter] = rtp_session_new(RTP_SESSION_SENDONLY);\n\n\t\trtp_session_set_scheduling_mode(m_Session[nCounter], 1);\n\t\trtp_session_set_blocking_mode(m_Session[nCounter], 0);\n\t\trtp_session_set_remote_addr(m_Session[nCounter], argv[2], m_nPort);\n\t\trtp_session_set_send_payload_type(m_Session[nCounter], 0);\n\n\t\tif (m_SSRC != NULL) {\n\t\t\trtp_session_set_ssrc(m_Session[nCounter], atoi(m_SSRC));\n\t\t}\n\n\t\tm_nPort += 2;\n\t}\n\n\tinfile = fopen(argv[1], \"rb\");\n\n\tif (infile == NULL) {\n\t\tprintf(\"==> Cannot open file !!!!\");\n\t\tSleep(1000);\n\t\treturn -1;\n\t}\n\n\t//\tprintf(\"==> Open file\\n\");\n\n\t/* Create a set */\n\tpSessionSet = session_set_new();\n\t//\tprintf(\"==> Session set\\n\");\n\n\twhile (((nCounter = (int)fread(m_pBuffer, 1, m_nPacket_Size, infile)) > 0) && (m_bExit == FALSE)) {\n\t\tint k;\n\t\t// g_message(\"Sending packet.\");\n\t\tfor (k = 0; k < m_nChannels; k++) {\n\t\t\t/* add the session to the set */\n\t\t\tsession_set_set(pSessionSet, m_Session[k]);\n\t\t\t// printf(\"==> Session set set %d\\n\", k);\n\t\t}\n\t\t/* and then suspend the process by selecting() */\n\t\tsession_set_select(NULL, pSessionSet, NULL);\n\t\t// printf(\"==> Session set select\\n\");\n\n\t\tfor (k = 0; k < m_nChannels; k++) {\n\t\t\t// printf(\"---\\n\");\n\t\t\t/* this is stupid to do this test, because all session work the same way,\n\t\t\tas the same user_ts is used for all sessions, here. */\n\t\t\tif (session_set_is_set(pSessionSet, m_Session[k])) {\n\t\t\t\t// printf(\"==> Session set is set %d\\n\", k);\n\t\t\t\trtp_session_send_with_ts(m_Session[k], m_pBuffer, nCounter, m_nUser_Timestamp);\n\t\t\t\t// g_message(\"packet sended !\");\n\t\t\t}\n\t\t}\n\t\tm_nUser_Timestamp += m_nTimestamp_Inc;\n\t}\n\n\tfclose(infile);\n\tprintf(\"==> Close file\\n\");\n\n\tfor (nCounter = 0; nCounter < m_nChannels; nCounter++) {\n\t\trtp_session_destroy(m_Session[nCounter]);\n\t}\n\n\tsession_set_destroy(pSessionSet);\n\n\t// Give us some time\n\tSleep(250);\n\n\tortp_exit();\n\tortp_global_stats_display();\n\n\tortp_free(m_pBuffer);\n\n\tprintf(\"==> Remove the CTRL-C handler...\\n\");\n\tSetConsoleCtrlHandler((PHANDLER_ROUTINE)ctrlHandlerFunction, FALSE);\n\n\t// Wait for an input key\n\tprintf(\"Waiting for exit : \");\n\n\tfor (nCounter = 0; nCounter < 4 * 5; nCounter++) {\n\t\tprintf(\".\");\n\t\tSleep(250);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tests/win_sender/RTPSender.vcproj",
    "content": "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\n<VisualStudioProject\n\tProjectType=\"Visual C++\"\n\tVersion=\"8.00\"\n\tName=\"RTPSender\"\n\tProjectGUID=\"{14A51171-516C-4D50-A88C-953B9B09F33F}\"\n\tRootNamespace=\"HPRTP_DIRECTX_oRTPSender\"\n\tKeyword=\"Win32Proj\"\n\t>\n\t<Platforms>\n\t\t<Platform\n\t\t\tName=\"Win32\"\n\t\t/>\n\t</Platforms>\n\t<ToolFiles>\n\t</ToolFiles>\n\t<Configurations>\n\t\t<Configuration\n\t\t\tName=\"Debug|Win32\"\n\t\t\tOutputDirectory=\"Debug\"\n\t\t\tIntermediateDirectory=\"Debug\"\n\t\t\tConfigurationType=\"1\"\n\t\t\tInheritedPropertySheets=\"$(VCInstallDir)VCProjectDefaults\\UpgradeFromVC71.vsprops\"\n\t\t\tUseOfMFC=\"2\"\n\t\t\tCharacterSet=\"0\"\n\t\t\t>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreBuildEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCustomBuildTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCMIDLTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\tOptimization=\"0\"\n\t\t\t\tAdditionalIncludeDirectories=\"&quot;$(SolutionDir)include&quot;;&quot;$(SolutionDir)include\\ortp&quot;;&quot;$(SolutionDir)src&quot;;&quot;$(SolutionDir)build\\win32native\\include&quot;\"\n\t\t\t\tPreprocessorDefinitions=\"WIN32;_DEBUG;_CONSOLE;WINDOW_NATIVE;_CRT_SECURE_NO_DEPRECATE\"\n\t\t\t\tMinimalRebuild=\"true\"\n\t\t\t\tBasicRuntimeChecks=\"3\"\n\t\t\t\tRuntimeLibrary=\"3\"\n\t\t\t\tUsePrecompiledHeader=\"0\"\n\t\t\t\tWarningLevel=\"3\"\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\n\t\t\t\tDebugInformationFormat=\"4\"\n\t\t\t\tCallingConvention=\"0\"\n\t\t\t\tCompileAs=\"2\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreLinkEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCLinkerTool\"\n\t\t\t\tAdditionalDependencies=\"oRTP.lib Ws2_32.lib Winmm.lib\"\n\t\t\t\tOutputFile=\"$(SolutionDir)Final/Exe/Debug/RTPSender.exe\"\n\t\t\t\tLinkIncremental=\"2\"\n\t\t\t\tAdditionalLibraryDirectories=\"$(SolutionDir)Final/Lib/Debug\"\n\t\t\t\tIgnoreAllDefaultLibraries=\"false\"\n\t\t\t\tIgnoreDefaultLibraryNames=\"libcmtd\"\n\t\t\t\tGenerateDebugInformation=\"true\"\n\t\t\t\tProgramDatabaseFile=\"$(OutDir)/RTPSender.pdb\"\n\t\t\t\tSubSystem=\"1\"\n\t\t\t\tTargetMachine=\"1\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCALinkTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManifestTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXDCMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCBscMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCFxCopTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCAppVerifierTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebDeploymentTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPostBuildEventTool\"\n\t\t\t/>\n\t\t</Configuration>\n\t\t<Configuration\n\t\t\tName=\"Release|Win32\"\n\t\t\tOutputDirectory=\"Release\"\n\t\t\tIntermediateDirectory=\"Release\"\n\t\t\tConfigurationType=\"1\"\n\t\t\tInheritedPropertySheets=\"$(VCInstallDir)VCProjectDefaults\\UpgradeFromVC71.vsprops\"\n\t\t\tCharacterSet=\"0\"\n\t\t\tWholeProgramOptimization=\"1\"\n\t\t\t>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreBuildEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCustomBuildTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCMIDLTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCCLCompilerTool\"\n\t\t\t\tAdditionalIncludeDirectories=\"&quot;$(SolutionDir)include&quot;;&quot;$(SolutionDir)include\\ortp&quot;;&quot;$(SolutionDir)src&quot;;&quot;$(SolutionDir)build\\win32native\\include&quot;\"\n\t\t\t\tPreprocessorDefinitions=\"WIN32;NDEBUG;_CONSOLE;WINDOW_NATIVE;_CRT_SECURE_NO_DEPRECATE\"\n\t\t\t\tRuntimeLibrary=\"0\"\n\t\t\t\tUsePrecompiledHeader=\"0\"\n\t\t\t\tWarningLevel=\"3\"\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\n\t\t\t\tDebugInformationFormat=\"0\"\n\t\t\t\tCallingConvention=\"0\"\n\t\t\t\tCompileAs=\"2\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCResourceCompilerTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPreLinkEventTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCLinkerTool\"\n\t\t\t\tAdditionalDependencies=\"oRTP.lib Ws2_32.lib Winmm.lib\"\n\t\t\t\tOutputFile=\"$(SolutionDir)Final/Exe/Release/RTPSender.exe\"\n\t\t\t\tLinkIncremental=\"1\"\n\t\t\t\tAdditionalLibraryDirectories=\"$(SolutionDir)Final/Lib/Release\"\n\t\t\t\tIgnoreAllDefaultLibraries=\"false\"\n\t\t\t\tGenerateDebugInformation=\"false\"\n\t\t\t\tSubSystem=\"1\"\n\t\t\t\tOptimizeReferences=\"2\"\n\t\t\t\tEnableCOMDATFolding=\"2\"\n\t\t\t\tTargetMachine=\"1\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCALinkTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCManifestTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCXDCMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCBscMakeTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCFxCopTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCAppVerifierTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCWebDeploymentTool\"\n\t\t\t/>\n\t\t\t<Tool\n\t\t\t\tName=\"VCPostBuildEventTool\"\n\t\t\t/>\n\t\t</Configuration>\n\t</Configurations>\n\t<References>\n\t</References>\n\t<Files>\n\t\t<Filter\n\t\t\tName=\"Source Files\"\n\t\t\tFilter=\"cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx\"\n\t\t\tUniqueIdentifier=\"{4FC737F1-C7A5-4376-A066-2A32D752A2FF}\"\n\t\t\t>\n\t\t\t<File\n\t\t\t\tRelativePath=\".\\RTPSender.cpp\"\n\t\t\t\t>\n\t\t\t</File>\n\t\t</Filter>\n\t\t<Filter\n\t\t\tName=\"Header Files\"\n\t\t\tFilter=\"h;hpp;hxx;hm;inl;inc;xsd\"\n\t\t\tUniqueIdentifier=\"{93995380-89BD-4b04-88EB-625FBE52EBFB}\"\n\t\t\t>\n\t\t</Filter>\n\t\t<Filter\n\t\t\tName=\"Resource Files\"\n\t\t\tFilter=\"rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx\"\n\t\t\tUniqueIdentifier=\"{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}\"\n\t\t\t>\n\t\t</Filter>\n\t</Files>\n\t<Globals>\n\t</Globals>\n</VisualStudioProject>\n"
  },
  {
    "path": "src/utils.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n#include \"math.h\"\n#include \"ortp/logging.h\"\n#include \"ortp/port.h\"\n#include \"utils.h\"\n\nuint64_t ortp_timeval_to_ntp(const struct timeval *tv) {\n\tuint64_t msw;\n\tuint64_t lsw;\n\tmsw = tv->tv_sec + 0x83AA7E80; /* 0x83AA7E80 is the number of seconds from 1900 to 1970 */\n\tlsw = (uint32_t)((double)tv->tv_usec * (double)(((uint64_t)1) << 32) * 1.0e-6);\n\treturn msw << 32 | lsw;\n}\n"
  },
  {
    "path": "src/utils.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef UTILS_H\n#define UTILS_H\n\n#include \"ortp/event.h\"\n#include \"ortp/rtpsession.h\"\n\nvoid ortp_init_logger(void);\nvoid ortp_uninit_logger(void);\n\n#define OList bctbx_list_t\n\n#define o_list_next(elem) ((elem)->next)\n#define o_list_prev(elem) ((elem)->prev)\n\n#define o_list_append bctbx_list_append\n#define o_list_prepend bctbx_list_prepend\n#define o_list_remove bctbx_list_remove\n#define o_list_free bctbx_list_free\n#define o_list_remove_link bctbx_list_erase_link\n#define o_list_free_with_data bctbx_list_free_with_data\n#define o_list_insert_sorted bctbx_list_insert_sorted\n\n#define o_list_free_func bctbx_list_free_func\n\n#define ORTP_POINTER_TO_INT(p) ((int)(intptr_t)(p))\n#define ORTP_INT_TO_POINTER(i) ((void *)(intptr_t)(i))\n\ntypedef struct _dwsplit_t {\n#ifdef ORTP_BIGENDIAN\n\tuint16_t hi;\n\tuint16_t lo;\n#else\n\tuint16_t lo;\n\tuint16_t hi;\n#endif\n} dwsplit_t;\n\ntypedef union {\n\tdwsplit_t split;\n\tuint32_t one;\n} poly32_t;\n\n#ifdef ORTP_BIGENDIAN\n#define hton24(x) (x)\n#else\n#define hton24(x) ((((x)&0x00ff0000) >> 16) | (((x)&0x000000ff) << 16) | ((x)&0x0000ff00))\n#endif\n#define ntoh24(x) hton24(x)\n\n#if defined(_WIN32) || defined(_WIN32_WCE)\n#define is_would_block_error(errnum) (errnum == WSAEWOULDBLOCK)\n#else\n#define is_would_block_error(errnum) (errnum == EWOULDBLOCK || errnum == EAGAIN)\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nvoid ortp_ev_queue_put(OrtpEvQueue *q, OrtpEvent *ev);\n\nuint64_t ortp_timeval_to_ntp(const struct timeval *tv);\n\nint _ortp_sendto(ortp_socket_t sockfd, mblk_t *m, int flags, const struct sockaddr *destaddr, socklen_t destlen);\nvoid rtcp_sdes_items_uninit(RtcpSdesItems *items);\n\nbool_t _rtcp_next_packet(mblk_t *m);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/videobandwidthestimator.cc",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"videobandwidthestimator.h\"\n#include \"ortp/logging.h\"\n#include \"ortp/rtpsession.h\"\n#include <math.h>\n\n#include <algorithm>\n#include <deque>\n#include <vector>\n\n#define MIN_DIFFTIME 0.00001f\n\nnamespace ortp {\n\nstruct VBEInProgressMeasurement {\n\tvoid reset() {\n\t\tmRtpTimestamp = 0;\n\t\tmFirstTimestamp = {0, 0};\n\t\tmLastTimestamp = {0, 0};\n\t\tmBytes = 0;\n\t\tmPackets = 0;\n\t}\n\tuint32_t mRtpTimestamp;\n\tstruct timeval mFirstTimestamp;\n\tstruct timeval mLastTimestamp;\n\tunsigned int mBytes;\n\tunsigned int mPackets;\n};\n\nstruct VBEMeasurement {\n\tfloat mBitrate;\n};\n\nstruct VBEMeasurementGreater {\n\tconstexpr bool operator()(const VBEMeasurement &m1, const VBEMeasurement &m2) const {\n\t\treturn m1.mBitrate > m2.mBitrate;\n\t}\n};\n\n/*\n * The VideoBandwidthEstimator processes video packets, and computes the time that was required to receive a full frame,\n * comprised of multiple RTP packets. This measurement is stored in container, that grows with the number of frames\n * received. Every mMinInterval seconds, it outputs an estimation by sorting the container and getting the value for\n * which mTrustPercetage of available measurements are above this value; so that we have high degree of confidence that\n * the real network bandwidth is near but above the estimated value.\n */\nclass VideoBandwidthEstimator {\npublic:\n\tVideoBandwidthEstimator(RtpSession *session) : mSession(session) {\n\t\treset();\n\t}\n\tvoid reset() {\n\t\tmLastEstimationTime.tv_sec = 0;\n\t\tmLastEstimationTime.tv_usec = 0;\n\t\tmCurrentMeasurement.reset();\n\t\tmMeasurements.clear();\n\t}\n\tvoid setMinPacketCount(unsigned int count) {\n\t\tmPacketCountMin = count;\n\t}\n\tunsigned int getMinPacketCount() const {\n\t\treturn mPacketCountMin;\n\t}\n\tvoid setTrustPercentage(unsigned int trust) {\n\t\tmTrustPercetage = trust;\n\t}\n\tunsigned int getTrustPercentage() const {\n\t\treturn mTrustPercetage;\n\t}\n\tvoid setMinMeasurements(unsigned int count) {\n\t\tmMinMeasurements = (size_t)count;\n\t\tmMaxMeasurements = mMinMeasurements * 3;\n\t}\n\tunsigned int getMinMeasurements() const {\n\t\treturn (unsigned int)mMinMeasurements;\n\t}\n\tfloat makeAvailableBandwidthEstimate();\n\tvoid processPacket(uint32_t sent_timestamp, const struct timeval *recv_timestamp, int msgsize, bool_t is_last);\n\tstatic VideoBandwidthEstimator *toCpp(OrtpVideoBandwidthEstimator *vbe) {\n\t\treturn reinterpret_cast<VideoBandwidthEstimator *>(vbe);\n\t}\n\tstatic const VideoBandwidthEstimator *toCpp(const OrtpVideoBandwidthEstimator *vbe) {\n\t\treturn reinterpret_cast<const VideoBandwidthEstimator *>(vbe);\n\t}\n\nprivate:\n\tvoid initializeMeasurement(uint32_t sent_timestamp, const struct timeval *recv_timestamp);\n\tvoid processMeasurement();\n\tvoid endMeasurement();\n\tbool periodElapsed(const struct timeval &now) {\n\t\tfloat duration = (now.tv_sec - mLastEstimationTime.tv_sec) +\n\t\t                 ((float)(now.tv_usec - mLastEstimationTime.tv_usec)) / 1000000.0f;\n\t\treturn duration >= (float)mMinInterval;\n\t}\n\tRtpSession *mSession = nullptr;\n\tunsigned int mPacketCountMin = 3;\n\tunsigned int mTrustPercetage = 90;\n\tsize_t mMinMeasurements = 70;\n\tsize_t mMaxMeasurements = 200;\n\tint mMinInterval = 5; // in seconds\n\tstruct timeval mLastEstimationTime {};\n\tVBEInProgressMeasurement mCurrentMeasurement;\n\tstd::deque<VBEMeasurement> mMeasurements;\n};\n\nfloat VideoBandwidthEstimator::makeAvailableBandwidthEstimate() {\n\tsize_t index = (mTrustPercetage * mMeasurements.size()) / 100;\n\tstd::vector<VBEMeasurement> sortedMeasurements(mMeasurements.size());\n\n\tstd::partial_sort_copy(mMeasurements.begin(), mMeasurements.end(), sortedMeasurements.begin(),\n\t                       sortedMeasurements.end(), VBEMeasurementGreater());\n\tfloat estimate = sortedMeasurements[index].mBitrate;\n\tortp_message(\"[VBE]: front: %f  back: %f, index: %i, size: %i, new estimate: %f bit/s\",\n\t             sortedMeasurements.front().mBitrate, sortedMeasurements.back().mBitrate, (int)index,\n\t             (int)sortedMeasurements.size(), estimate);\n\treturn estimate;\n}\n\nvoid VideoBandwidthEstimator::processMeasurement() {\n\tfloat difftime = (float)(mCurrentMeasurement.mLastTimestamp.tv_sec - mCurrentMeasurement.mFirstTimestamp.tv_sec) +\n\t                 1e-6f * (mCurrentMeasurement.mLastTimestamp.tv_usec - mCurrentMeasurement.mFirstTimestamp.tv_usec);\n\n\tif (difftime > MIN_DIFFTIME) {\n\t\tif (mLastEstimationTime.tv_sec == 0) {\n\t\t\tmLastEstimationTime = mCurrentMeasurement.mLastTimestamp;\n\t\t}\n\n\t\tfloat bitrate = (mCurrentMeasurement.mBytes * 8 / difftime);\n\t\tmMeasurements.emplace_front(VBEMeasurement{bitrate});\n\n\t\tif (mMeasurements.size() > mMaxMeasurements) {\n\t\t\t/* remove the oldest measurement */\n\t\t\tmMeasurements.pop_back();\n\t\t}\n\n\t\t// ortp_message(\"VBE: added measure of %f bit/s\", bitrate);\n\n\t\t/* when interval expires, and provided that we have at least mMinMeasurements, make an estimate*/\n\t\tif (mMeasurements.size() > mMinMeasurements && periodElapsed(mCurrentMeasurement.mLastTimestamp)) {\n\t\t\tOrtpEvent *ev = ortp_event_new(ORTP_EVENT_NEW_VIDEO_BANDWIDTH_ESTIMATION_AVAILABLE);\n\t\t\tOrtpEventData *ed = ortp_event_get_data(ev);\n\n\t\t\tmLastEstimationTime = mCurrentMeasurement.mLastTimestamp;\n\t\t\ted->info.video_bandwidth_available = makeAvailableBandwidthEstimate();\n\t\t\tortp_debug(\n\t\t\t    \"[VBE] Dispatching event ORTP_EVENT_NEW_VIDEO_BANDWIDTH_ESTIMATION_AVAILABLE with value %f kbits/s\",\n\t\t\t    ed->info.video_bandwidth_available / 1000);\n\t\t\trtp_session_dispatch_event(mSession, ev);\n\t\t}\n\t} else {\n\t\t// The mesaurement cannot be used.\n\t}\n}\n\nvoid VideoBandwidthEstimator::initializeMeasurement(uint32_t sent_timestamp, const struct timeval *recv_timestamp) {\n\tmCurrentMeasurement.mRtpTimestamp = sent_timestamp;\n\tmCurrentMeasurement.mFirstTimestamp = *recv_timestamp;\n\tmCurrentMeasurement.mPackets = 1;\n\t// ignore bytes received: this is the first packet\n}\n\nvoid VideoBandwidthEstimator::endMeasurement() {\n\tif (mCurrentMeasurement.mPackets >= mPacketCountMin) {\n\t\tprocessMeasurement();\n\t} // else not enough packets for this measurement, drop it.\n\tmCurrentMeasurement.reset();\n}\n\nvoid VideoBandwidthEstimator::processPacket(uint32_t sent_timestamp,\n                                            const struct timeval *recv_timestamp,\n                                            int msgsize,\n                                            bool_t is_last) {\n\tif (mCurrentMeasurement.mPackets == 0) {\n\t\tinitializeMeasurement(sent_timestamp, recv_timestamp);\n\t} else if (mCurrentMeasurement.mRtpTimestamp == sent_timestamp) {\n\t\tmCurrentMeasurement.mBytes += msgsize;\n\t\tmCurrentMeasurement.mPackets++;\n\t\tmCurrentMeasurement.mLastTimestamp = *recv_timestamp;\n\t} else {\n\t\t// Special case where the timestamp is discontinued. The current measurement may be used.\n\t\tendMeasurement();\n\t\t// And restart a new one.\n\t\tinitializeMeasurement(sent_timestamp, recv_timestamp);\n\t}\n\tif (is_last) {\n\t\tendMeasurement();\n\t}\n}\n\n} // namespace ortp\n\nusing namespace ortp;\n\nOrtpVideoBandwidthEstimator *ortp_video_bandwidth_estimator_new(RtpSession *session) {\n\tauto vbe = new VideoBandwidthEstimator(session);\n\treturn (OrtpVideoBandwidthEstimator *)vbe;\n}\n\nvoid ortp_video_bandwidth_estimator_destroy(OrtpVideoBandwidthEstimator *vbe) {\n\tdelete VideoBandwidthEstimator::toCpp(vbe);\n}\n\nvoid ortp_video_bandwidth_estimator_reset(OrtpVideoBandwidthEstimator *vbe) {\n\tVideoBandwidthEstimator::toCpp(vbe)->reset();\n}\n\nvoid ortp_video_bandwidth_estimator_set_packets_count_min(OrtpVideoBandwidthEstimator *vbe, unsigned int value) {\n\tVideoBandwidthEstimator::toCpp(vbe)->setMinPacketCount(value);\n}\n\nvoid ortp_video_bandwidth_estimator_set_trust(OrtpVideoBandwidthEstimator *vbe, unsigned int value) {\n\tVideoBandwidthEstimator::toCpp(vbe)->setTrustPercentage(value);\n}\n\nvoid ortp_video_bandwidth_estimator_set_min_measurements_count(OrtpVideoBandwidthEstimator *vbe, unsigned int value) {\n\tVideoBandwidthEstimator::toCpp(vbe)->setMinMeasurements(value);\n}\n\nunsigned int ortp_video_bandwidth_estimator_get_packets_count_min(OrtpVideoBandwidthEstimator *vbe) {\n\treturn VideoBandwidthEstimator::toCpp(vbe)->getMinPacketCount();\n}\n\nunsigned int ortp_video_bandwidth_estimator_get_min_measurements_count(const OrtpVideoBandwidthEstimator *vbe) {\n\treturn VideoBandwidthEstimator::toCpp(vbe)->getMinMeasurements();\n}\n\nunsigned int ortp_video_bandwidth_estimator_get_trust(OrtpVideoBandwidthEstimator *vbe) {\n\treturn VideoBandwidthEstimator::toCpp(vbe)->getTrustPercentage();\n}\n\nfloat ortp_video_bandwidth_estimator_get_estimated_available_bandwidth(OrtpVideoBandwidthEstimator *vbe) {\n\treturn VideoBandwidthEstimator::toCpp(vbe)->makeAvailableBandwidthEstimate();\n}\n\nvoid ortp_video_bandwidth_estimator_process_packet(OrtpVideoBandwidthEstimator *vbe,\n                                                   uint32_t sent_timestamp,\n                                                   const struct timeval *recv_timestamp,\n                                                   int msgsize,\n                                                   bool_t is_last) {\n\tVideoBandwidthEstimator::toCpp(vbe)->processPacket(sent_timestamp, recv_timestamp, msgsize, is_last);\n}\n"
  },
  {
    "path": "src/videobandwidthestimator.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef VIDEOBANDWIDTHESTIMATOR_H\n#define VIDEOBANDWIDTHESTIMATOR_H\n\n#include <bctoolbox/list.h>\n#include <ortp/port.h>\n#include <ortp/utils.h>\n\ntypedef struct _OrtpVideoBandwidthEstimator OrtpVideoBandwidthEstimator;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nOrtpVideoBandwidthEstimator *ortp_video_bandwidth_estimator_new(struct _RtpSession *session);\n\nvoid ortp_video_bandwidth_estimator_destroy(OrtpVideoBandwidthEstimator *vbe);\n\nvoid ortp_video_bandwidth_estimator_reset(OrtpVideoBandwidthEstimator *vbe);\n\n/**\n * Sets the minimum number of packets with the same sent timestamp to be processed continuously before being used.\n *\n */\nvoid ortp_video_bandwidth_estimator_set_packets_count_min(OrtpVideoBandwidthEstimator *vbe, unsigned int value);\n\n/**\n * Sets the number of measurements needed to compute the available video bandwidth.\n *\n */\nvoid ortp_video_bandwidth_estimator_set_min_measurements_count(OrtpVideoBandwidthEstimator *vbe, unsigned int value);\n\n/**\n * Sets the percentage for which the chosen bandwidth value in all available will be inferior.\n * Example: for 100 packets with 90% trust, bandwidth value will be the 90th after sorted.\n * Default value is 90.\n */\nvoid ortp_video_bandwidth_estimator_set_trust(OrtpVideoBandwidthEstimator *vbe, unsigned int value);\n\n/**\n * Gets the minimum number of packets with the same sent timestamp to be processed continuously before being used.\n *\n */\nunsigned int ortp_video_bandwidth_estimator_get_packets_count_min(OrtpVideoBandwidthEstimator *vbe);\n\n/**\n * Gets the percentage for which the chosen bandwidth value in all available will be inferior.\n * Example: for 100 packets with 90% trust, bandwidth value will be the 90th after sorted.\n * Default value is 90.\n */\nunsigned int ortp_video_bandwidth_estimator_get_trust(OrtpVideoBandwidthEstimator *vbe);\n\n/**\n * Gets the number of measurements needed to compute the available video bandwidth.\n * Default value is 50.\n */\nunsigned int ortp_video_bandwidth_estimator_get_min_measurements_count(OrtpVideoBandwidthEstimator *vbe);\n\nvoid ortp_video_bandwidth_estimator_process_packet(OrtpVideoBandwidthEstimator *vbe,\n                                                   uint32_t sent_timestamp,\n                                                   const struct timeval *recv_timestamp,\n                                                   int msgsize,\n                                                   bool_t is_last);\n\nfloat ortp_video_bandwidth_estimator_get_estimated_available_bandwidth(OrtpVideoBandwidthEstimator *vbe);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/winrttimer.cpp",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n#include <windows.h>\n\n#include \"ortp/logging.h\"\n#include \"winrttimer.h\"\n\n#ifndef ORTP_WINDOWS_DESKTOP\n\n#ifdef ORTP_WINDOWS_PHONE\n#using < Windows.winmd>\n#endif\n\nusing namespace Windows::Foundation;\nusing namespace Windows::System::Threading;\n\nclass WinRTTimer {\npublic:\n\tWinRTTimer();\n\t~WinRTTimer();\n\tvoid run();\n\nprivate:\n\tThreadPoolTimer ^ PeriodicTimer;\n\tHANDLE SleepEvent;\n\tULONGLONG LateTicks;\n\tULONGLONG PosixTimerTime;\n\tULONGLONG OffsetTime;\n};\n\nWinRTTimer::WinRTTimer() : LateTicks(0), PosixTimerTime(0), OffsetTime(GetTickCount64()) {\n\tTimeSpan period;\n\tperiod.Duration = TIME_INTERVAL * 10000;\n\tSleepEvent = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);\n\tPeriodicTimer = ThreadPoolTimer::CreatePeriodicTimer(ref new TimerElapsedHandler([this](ThreadPoolTimer ^ source) {\n\t\t                                                     if (source == PeriodicTimer) {\n\t\t\t                                                     PosixTimerTime += TIME_INTERVAL;\n\t\t                                                     }\n\t                                                     }),\n\t                                                     period);\n}\n\nWinRTTimer::~WinRTTimer() {\n\tPeriodicTimer->Cancel();\n}\n\nvoid WinRTTimer::run() {\n\t// If timer have expired while we where out of this method\n\t// Try to run after lost time.\n\tif (LateTicks > 0) {\n\t\tLateTicks--;\n\t\tPosixTimerTime += TIME_INTERVAL;\n\t\treturn;\n\t}\n\n\tULONGLONG diff = GetTickCount64() - PosixTimerTime - OffsetTime;\n\tif (diff > TIME_INTERVAL) {\n\t\tLateTicks = diff / TIME_INTERVAL;\n\t\tortp_warning(\"We must catchup %i ticks.\", LateTicks);\n\t\treturn;\n\t}\n\n\tWaitForSingleObjectEx(SleepEvent, TIME_TIMEOUT, FALSE);\n}\n\nstatic WinRTTimer *timer;\n\nvoid winrt_timer_init(void) {\n\ttimer = new WinRTTimer();\n}\n\nvoid winrt_timer_do(void) {\n\ttimer->run();\n}\n\nvoid winrt_timer_close(void) {\n\tdelete timer;\n}\n\n#endif\n"
  },
  {
    "path": "src/winrttimer.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid winrt_timer_init(void);\nvoid winrt_timer_do(void);\nvoid winrt_timer_close(void);\n\n#ifdef __cplusplus\n};\n#endif\n\n#define TIME_INTERVAL 50\n#define TIME_TIMEOUT 100\n"
  },
  {
    "path": "tester/CMakeLists.txt",
    "content": "############################################################################\n# Copyright (c) 2010-2023 Belledonne Communications SARL.\n#\n# This file is part of oRTP \n# (see https://gitlab.linphone.org/BC/public/ortp).\n#\n############################################################################\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as\n# published by the Free Software Foundation, either version 3 of the\n# License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. If not, see <http://www.gnu.org/licenses/>.\n############################################################################\n\nset(ORTP_LIBS_FOR_TESTER ortp)\n\nif(WIN32)\n\t#required for htons()\n\tlist(APPEND ORTP_LIBS_FOR_TESTER Ws2_32)\nendif()\n\nset(RAW_FILES\n\traw/h265-iframe\n)\n\nset(SOURCE_FILES_C\n\tortp_tester.c\n\textension_header_tester.c\n\trtp_tester.c\n)\n\nset(IOS_RESOURCE_FILE)\nset(SOURCE_FILES_CXX\n\tbundle_tester.cc\n\tfec_tester.cc\n\tortp_tester_utils.cc\n)\nset(SOURCE_FILES_OBJC)\n\nadd_definitions(-DBCTBX_LOG_DOMAIN=\"ortp\")\n\nbc_apply_compile_flags(SOURCE_FILES_C STRICT_OPTIONS_CPP STRICT_OPTIONS_C)\nbc_apply_compile_flags(SOURCE_FILES_CXX STRICT_OPTIONS_CPP STRICT_OPTIONS_CXX)\nbc_apply_compile_flags(SOURCE_FILES_OBJC STRICT_OPTIONS_CPP STRICT_OPTIONS_OBJC)\n\nif(CMAKE_SYSTEM_NAME STREQUAL \"WindowsStore\")\n\tadd_library(ortp-tester-static STATIC ${SOURCE_FILES_C} ${SOURCE_FILES_CXX})\n\ttarget_link_libraries(ortp-tester-static PRIVATE ${ORTP_LIBS_FOR_TESTER} PUBLIC ${BCToolbox_tester_TARGET})\n\t\n\t# TODO : implement tester-runtime like bellesip\n\t#install(FILES \"${CMAKE_CURRENT_BINARY_DIR}/BelledonneCommunications.ortp.Tester.winmd\" DESTINATION lib)\n\n\t#install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Debug/BelledonneCommunications.ortp.Tester.pdb\n\t#\tDESTINATION ${CMAKE_INSTALL_LIBDIR}\n\t#\tPERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE\n\t#\tCONFIGURATIONS Debug RelWithDebInfo\n\t#)\nelse()\n\tif(IOS)\n\t\tset_source_files_properties(${IOS_RESOURCES_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)\n\t\tadd_executable(ortp-tester MACOSX_BUNDLE ${IOS_RESOURCES_FILES} ${SOURCE_FILES_C} ${SOURCE_FILES_CXX} ${SOURCE_FILES_OBJC})\n\t\tset_target_properties(ortp-tester PROPERTIES\n\t\t\tMACOSX_FRAMEWORK_INFO_PLIST Info.plist.in\n\t\t\tMACOSX_BUNDLE_BUNDLE_NAME org.linphone.ortp.ortp-tester\n\t\t\tMACOSX_BUNDLE_BUNDLE_VERSION 1.0\n\t\t\tMACOSX_BUNDLE_SHORT_VERSION_STRING \"1.0\")\n\n\telse()\n\t\tadd_executable(ortp-tester ${SOURCE_FILES_C} ${SOURCE_FILES_CXX} ${SOURCE_FILES_OBJC})\n\tendif()\n\n\tset_target_properties(ortp-tester PROPERTIES LINKER_LANGUAGE CXX)\n\ttarget_link_libraries(ortp-tester PRIVATE ${ORTP_LIBS_FOR_TESTER} ${BCToolbox_tester_TARGET})\n\n\tif(NOT IOS)\n\t\tinstall(TARGETS ortp-tester\n\t\t\tRUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}\n\t\t\tLIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\n\t\t\tARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}\n\t\t\tPERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE\n\t\t)\n\t\tinstall(FILES ${RAW_FILES} DESTINATION \"${CMAKE_INSTALL_DATADIR}/ortp-tester/raw\")\n\tendif()\nendif()\n"
  },
  {
    "path": "tester/bundle_tester.cc",
    "content": "/*\n * Copyright (c) 2010-2024 Belledonne Communications SARL.\n *\n * This file is part of ortp\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <bctoolbox/defs.h>\n\n#include \"ortp/rtpsession.h\"\n#include \"ortp_tester.h\"\n#include \"ortp_tester_utils.h\"\n#include \"rtpbundle.h\"\n\nstatic void add_sessions() {\n\tRtpBundleCxx bundle;\n\n\tauto *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tbundle.addSession(\"main\", session);\n\n\tBC_ASSERT_TRUE(bundle.findSession(session));\n\tBC_ASSERT_STRING_EQUAL(bundle.getSessionMid(session).c_str(), \"main\");\n\n\tauto *session2 = rtp_session_new(RTP_SESSION_SENDRECV);\n\tbundle.addSession(\"secondary\", session2);\n\n\tBC_ASSERT_TRUE(bundle.findSession(session2));\n\tBC_ASSERT_STRING_EQUAL(bundle.getSessionMid(session2).c_str(), \"secondary\");\n\n\tauto *session3 = rtp_session_new(RTP_SESSION_SENDONLY);\n\tbundle.addSession(\"secondary\", session3);\n\n\tBC_ASSERT_TRUE(bundle.findSession(session3));\n\tBC_ASSERT_STRING_EQUAL(bundle.getSessionMid(session3).c_str(), \"secondary\");\n\n\tbundle.removeSession(session);\n\n\tBC_ASSERT_FALSE(bundle.findSession(session));\n\tBC_ASSERT_PTR_NULL(bundle.getPrimarySession());\n\n\tbundle.removeSessions(\"secondary\");\n\n\tBC_ASSERT_FALSE(bundle.findSession(session2));\n\tBC_ASSERT_FALSE(bundle.findSession(session3));\n\n\trtp_session_destroy(session);\n\trtp_session_destroy(session2);\n\trtp_session_destroy(session3);\n}\n\nstatic void primary_change() {\n\tRtpBundleCxx bundle;\n\n\tauto *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tbundle.addSession(\"main\", session);\n\n\tBC_ASSERT_PTR_EQUAL(bundle.getPrimarySession(), session);\n\n\tauto *newSession = rtp_session_new(RTP_SESSION_SENDRECV);\n\tbundle.addSession(\"main2\", newSession);\n\n\tBC_ASSERT_PTR_EQUAL(bundle.getPrimarySession(), session);\n\n\tbundle.setPrimarySession(newSession);\n\n\tBC_ASSERT_PTR_EQUAL(bundle.getPrimarySession(), newSession);\n\n\tbundle.clear();\n\n\trtp_session_destroy(session);\n\trtp_session_destroy(newSession);\n}\n\nstatic void dispatch_packet() {\n\tRtpBundleCxx bundle;\n\n\tRtpProfile profile = {};\n\trtp_profile_set_payload(&profile, 90, &payload_type_opus);\n\n\tauto *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tint pt_number = rtp_profile_get_payload_number_from_mime(&profile, \"opus\");\n\trtp_session_set_payload_type(session, pt_number);\n\n\t// Add a session that already knows it's ssrc.\n\tbundle.addSession(\"main\", session);\n\n\tRtpProfile profile2 = {};\n\trtp_profile_set_payload(&profile2, 96, &payload_type_vp8);\n\n\tauto *session2 = rtp_session_new(RTP_SESSION_SENDRECV);\n\tint pt_number2 = rtp_profile_get_payload_number_from_mime(&profile2, \"VP8\");\n\trtp_session_set_payload_type(session2, pt_number2);\n\n\t// Add a session that do not know it's ssrc yet.\n\tbundle.addSession(\"secondary\", session2);\n\n\t// Using the wanted session to create the packet and automatically add the correct mid.\n\tauto packet = rtp_session_create_packet_header(session, 0);\n\trtp_set_payload_type(packet, 90);\n\trtp_set_ssrc(packet, 1021991);\n\n\t// That packet should not be dispatched and be returned as it for the primary session.\n\tpacket = bundle.dispatch(true, packet).value_or(nullptr);\n\tBC_ASSERT_PTR_NOT_NULL(packet);\n\tBC_ASSERT_EQUAL(session->rtp.gs.bundleq.q_mcount, 0, int, \"%d\");\n\n\tfreemsg(packet);\n\n\tpacket = rtp_session_create_packet_header(session2, 0);\n\trtp_set_payload_type(packet, 96);\n\trtp_set_ssrc(packet, 18173254);\n\n\t// That packet should be dispatched and present in the bundleq.\n\tBC_ASSERT_FALSE(bundle.dispatch(true, packet).has_value());\n\tBC_ASSERT_EQUAL(session2->rtp.gs.bundleq.q_mcount, 1, int, \"%d\");\n\n\t// And the session should now know it's recv ssrc.\n\tBC_ASSERT_TRUE(session2->ssrc_set);\n\tBC_ASSERT_EQUAL(session2->rcv.ssrc, 18173254, uint32_t, \"%d\");\n\n\tpacket = rtp_session_create_packet_header(session, 0);\n\trtp_set_payload_type(packet, 96);\n\trtp_set_ssrc(packet, 78986545);\n\n\t// That packet has an unkown ssrc, it should be dispatched but is freed and no session received it.\n\tBC_ASSERT_FALSE(bundle.dispatch(true, packet).has_value());\n\tBC_ASSERT_EQUAL(session->rtp.gs.bundleq.q_mcount, 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(session2->rtp.gs.bundleq.q_mcount, 1, int, \"%d\");\n\n\tbundle.clear();\n\n\trtp_session_destroy(session);\n\trtp_session_destroy(session2);\n}\n\nstatic void dispatch_packet_without_mid() {\n\tRtpBundleCxx bundle;\n\n\tauto *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\n\tbundle.addSession(\"main\", session);\n\n\tRtpProfile profile = {};\n\trtp_profile_set_payload(&profile, 96, &payload_type_vp8);\n\n\tauto *session2 = rtp_session_new(RTP_SESSION_SENDRECV);\n\tint pt_number = rtp_profile_get_payload_number_from_mime(&profile, \"VP8\");\n\trtp_session_set_payload_type(session2, pt_number);\n\n\t// Send a first packet with the mid that we'll use for session2.\n\tauto *packet = rtp_session_create_packet_header(session2, 0);\n\trtp_set_payload_type(packet, 96);\n\trtp_set_ssrc(packet, 18173254);\n\n\tauto *mid = \"secondary\";\n\trtp_add_extension_header(packet, RTP_EXTENSION_MID, strlen(mid), (uint8_t *)mid);\n\n\t// That packet should be dispatched but freed because the session isn't in the bundle.\n\tBC_ASSERT_FALSE(bundle.dispatch(true, packet).has_value());\n\tBC_ASSERT_EQUAL(session->rtp.gs.bundleq.q_mcount, 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(session2->rtp.gs.bundleq.q_mcount, 0, int, \"%d\");\n\n\t// Now we add our session.\n\tbundle.addSession(\"secondary\", session2);\n\n\t// Creating a new packet but removing the mid to make sure it isn't there.\n\t// Since the bundle now knows that this ssrc has the mid \"secondary\" it should correctly assign the, now added,\n\t// session.\n\tpacket = rtp_session_create_packet_header(session2, 0);\n\trtp_set_payload_type(packet, 96);\n\trtp_set_ssrc(packet, 18173254);\n\n\trtp_delete_extension_header(packet, RTP_EXTENSION_MID);\n\n\t// That packet should be dispatched and present in bundleq.\n\tBC_ASSERT_FALSE(bundle.dispatch(true, packet).has_value());\n\tBC_ASSERT_EQUAL(session2->rtp.gs.bundleq.q_mcount, 1, int, \"%d\");\n\n\tbundle.clear();\n\n\trtp_session_destroy(session);\n\trtp_session_destroy(session2);\n}\n\nstatic void dispatch_rtcp_packet_with_referred_ssrc_assigned_base(bool assigned) {\n\tRtpBundleCxx bundle;\n\n\tauto *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tsession->ssrc_set = TRUE;\n\tsession->rcv.ssrc = 1021991;\n\tbundle.addSession(\"main\", session);\n\n\t// A SENDONLY session is already assigned as we know it's ssrc\n\tauto *session2 = rtp_session_new(RTP_SESSION_SENDONLY);\n\tsession2->rcv.ssrc = 19112024; // So that the dummy RTCP function have a ssrc to set\n\tbundle.addSession(\"secondary\", session2);\n\n\tRtpProfile profile = {};\n\trtp_profile_set_payload(&profile, 97, &payload_type_av1);\n\n\tauto *session3 = rtp_session_new(RTP_SESSION_SENDRECV);\n\tint pt_number = rtp_profile_get_payload_number_from_mime(&profile, \"AV1\");\n\trtp_session_set_payload_type(session3, pt_number);\n\tbundle.addSession(\"tertiary\", session3);\n\n\tif (assigned) {\n\t\t// Send a packet so that this session is assigned otherwise, referring won't work\n\t\tauto *packet = rtp_session_create_packet_header(session3, 0);\n\t\trtp_set_payload_type(packet, 97);\n\t\trtp_set_ssrc(packet, 22081992);\n\n\t\tBC_ASSERT_FALSE(bundle.dispatch(true, packet).has_value());\n\t\tBC_ASSERT_EQUAL(session3->rtp.gs.bundleq.q_mcount, 1, int, \"%d\");\n\t}\n\n\t// Create a RTCP packet but is referring session2\n\tauto *rtcpPacket = ortp_tester_make_dummy_rtcp_fb_pli(session2);\n\n\t// Packet should be dispatched and in the RCTP bundle queue of session2\n\tBC_ASSERT_FALSE(bundle.dispatch(false, rtcpPacket).has_value());\n\tBC_ASSERT_EQUAL(session->rtcp.gs.bundleq.q_mcount, 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(session2->rtcp.gs.bundleq.q_mcount, 1, int, \"%d\");\n\tBC_ASSERT_EQUAL(session3->rtcp.gs.bundleq.q_mcount, 0, int, \"%d\");\n\n\t// Create another RTCP packet at session3\n\trtcpPacket = ortp_tester_make_dummy_rtcp_fb_pli(session3);\n\n\tBC_ASSERT_FALSE(bundle.dispatch(false, rtcpPacket).has_value());\n\tBC_ASSERT_EQUAL(session->rtcp.gs.bundleq.q_mcount, 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(session2->rtcp.gs.bundleq.q_mcount, 1, int, \"%d\");\n\t// Packet should be dispatched in the RCTP bundle queue of session3 thanks to the referred SSRC in RR block.\n\tBC_ASSERT_EQUAL(session3->rtcp.gs.bundleq.q_mcount, 1, int, \"%d\");\n\n\t// If we test without session3 assignation we have to make a compound RTCP packet that contains session3's mid via\n\t// SDES so that it can be routed correctly\n\tif (!assigned) {\n\t\tsession3->rcv.ssrc = 22081992;\n\t\trtcpPacket = ortp_tester_make_dummy_sr(session3);\n\n\t\tBC_ASSERT_FALSE(bundle.dispatch(false, rtcpPacket).has_value());\n\t\tBC_ASSERT_EQUAL(session->rtcp.gs.bundleq.q_mcount, 0, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(session2->rtcp.gs.bundleq.q_mcount, 1, int, \"%d\");\n\t\t// Packet should be dispatched and in the RCTP bundle queue of session3\n\t\t// Only 1 even though the compound is in 3 parts (SR, SDES, PLI) because in this dummy packet, only the PLI is\n\t\t// referring to another ssrc\n\t\tBC_ASSERT_EQUAL(session3->rtcp.gs.bundleq.q_mcount, 2, int, \"%d\");\n\t}\n\n\tbundle.clear();\n\n\trtp_session_destroy(session);\n\trtp_session_destroy(session2);\n\trtp_session_destroy(session3);\n}\n\nstatic void dispatch_rtcp_packet_with_referred_ssrc_assigned() {\n\tdispatch_rtcp_packet_with_referred_ssrc_assigned_base(true);\n}\n\nstatic void dispatch_rtcp_packet_with_referred_ssrc_unassigned() {\n\tdispatch_rtcp_packet_with_referred_ssrc_assigned_base(false);\n}\n\nstatic void on_incoming_ssrc_in_bundle(BCTBX_UNUSED(RtpSession *session), void *mp, void *s, void *userData) {\n\tconst auto *m = static_cast<mblk_t *>(mp);\n\n\tuint8_t *data;\n\tconst size_t midSize = rtp_get_extension_header(m, RTP_EXTENSION_MID, &data);\n\tBC_ASSERT_GREATER(midSize, 0, size_t, \"%zu\");\n\n\tconst auto mid = std::string{reinterpret_cast<char *>(data), midSize};\n\tBC_ASSERT_STRING_EQUAL(mid.c_str(), \"secondary\");\n\n\tauto **newSessionForBundle = static_cast<RtpSession **>(s);\n\tauto **newSession = static_cast<RtpSession **>(userData);\n\n\t*newSessionForBundle = *newSession = rtp_session_new(RTP_SESSION_SENDRECV);\n}\n\nstatic void dispatch_new_ssrc_with_incoming_callback_set() {\n\tRtpBundleCxx bundle;\n\n\tauto *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tbundle.addSession(\"main\", session);\n\n\tauto *session2 = rtp_session_new(RTP_SESSION_SENDRECV);\n\tbundle.addSession(\"secondary\", session2);\n\n\t// Set the incoming ssrc callback into the bundle primary session.\n\tauto *primary = bundle.getPrimarySession();\n\tBC_ASSERT_PTR_EQUAL(primary, session);\n\n\tRtpSession *newSession = nullptr;\n\trtp_session_signal_connect(primary, \"new_incoming_ssrc_found_in_bundle\", on_incoming_ssrc_in_bundle, &newSession);\n\n\t// Send a packet with the \"secondary\" mid but another ssrc.\n\tauto *packet = rtp_session_create_packet_header(session2, 0);\n\trtp_set_ssrc(packet, 36587422);\n\n\t// Nothing is returned as it has been dispatched\n\tBC_ASSERT_FALSE(bundle.dispatch(true, packet).has_value());\n\n\t// The callback should have been called and set newSession to the new session created.\n\tif (BC_ASSERT_PTR_NOT_NULL(newSession)) {\n\t\tBC_ASSERT_TRUE(bundle.findSession(newSession));\n\t\tBC_ASSERT_TRUE(newSession->ssrc_set);\n\t\tBC_ASSERT_EQUAL(newSession->rcv.ssrc, 36587422, uint32_t, \"%u\");\n\t\tBC_ASSERT_EQUAL(newSession->rtp.gs.bundleq.q_mcount, 1, int, \"%d\");\n\t}\n\n\trtp_session_signal_disconnect_by_callback(primary, \"new_incoming_ssrc_found_in_bundle\", on_incoming_ssrc_in_bundle);\n\n\tbundle.clear();\n\n\trtp_session_destroy(session);\n\trtp_session_destroy(session2);\n\tif (newSession != nullptr) rtp_session_destroy(newSession);\n}\n\nstatic void look_out_for_outgoing_session() {\n\tRtpBundleCxx bundle;\n\n\tauto *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tbundle.addSession(\"main\", session);\n\n\tauto *session2 = rtp_session_new(RTP_SESSION_SENDONLY);\n\tbundle.addSession(\"secondary\", session2);\n\n\t// Create a packet with \"secondary\" mid and session2 sender.\n\tauto *packet = rtp_session_create_packet_header(session2, 0);\n\n\t// checkForSession should return the session2.\n\tBC_ASSERT_PTR_EQUAL(bundle.checkForSession(packet, true, true), session2);\n\n\tfreemsg(packet);\n\n\tbundle.clear();\n\n\trtp_session_destroy(session);\n\trtp_session_destroy(session2);\n}\n\nstatic void on_outgoing_ssrc_in_bundle(BCTBX_UNUSED(RtpSession *session), void *mp, void *s, void *userData) {\n\tconst auto *m = static_cast<mblk_t *>(mp);\n\n\tuint8_t *data;\n\tconst size_t midSize = rtp_get_extension_header(m, RTP_EXTENSION_MID, &data);\n\tBC_ASSERT_GREATER(midSize, 0, size_t, \"%zu\");\n\n\tconst auto mid = std::string{reinterpret_cast<char *>(data), midSize};\n\tBC_ASSERT_STRING_EQUAL(mid.c_str(), \"secondary\");\n\n\tauto **newSessionForBundle = static_cast<RtpSession **>(s);\n\tauto **newSession = static_cast<RtpSession **>(userData);\n\n\t*newSessionForBundle = *newSession = rtp_session_new(RTP_SESSION_SENDONLY);\n}\n\nstatic void look_out_for_outgoing_session_with_outgoing_callback_set() {\n\tRtpBundleCxx bundle;\n\n\tauto *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tbundle.addSession(\"main\", session);\n\n\tauto *session2 = rtp_session_new(RTP_SESSION_SENDONLY);\n\tbundle.addSession(\"secondary\", session2);\n\n\t// Set the outgoing ssrc onto the bundle primary session\n\tauto *primary = bundle.getPrimarySession();\n\tBC_ASSERT_PTR_EQUAL(primary, session);\n\n\tRtpSession *newSession = nullptr;\n\trtp_session_signal_connect(primary, \"new_outgoing_ssrc_found_in_bundle\", on_outgoing_ssrc_in_bundle, &newSession);\n\n\t// Send a packet with the \"secondary\" mid but another ssrc.\n\tauto *packet = rtp_session_create_packet_header(session2, 0);\n\trtp_set_ssrc(packet, 36587422);\n\n\t// checkForSession should return the new session.\n\tconst auto *returnedSession = bundle.checkForSession(packet, true, true);\n\n\tBC_ASSERT_PTR_NOT_NULL(newSession);\n\tBC_ASSERT_PTR_EQUAL(returnedSession, newSession);\n\n\trtp_session_signal_disconnect_by_callback(primary, \"new_outgoing_ssrc_found_in_bundle\", on_outgoing_ssrc_in_bundle);\n\n\tfreemsg(packet);\n\n\tbundle.clear();\n\n\trtp_session_destroy(session);\n\trtp_session_destroy(session2);\n\tif (newSession != nullptr) rtp_session_destroy(newSession);\n}\n\nstatic test_t tests[] = {\n    TEST_NO_TAG(\"Add sessions\", add_sessions),\n    TEST_NO_TAG(\"Primary change\", primary_change),\n    TEST_NO_TAG(\"Dispatch packet\", dispatch_packet),\n    TEST_NO_TAG(\"Dispatch packet without mid\", dispatch_packet_without_mid),\n    TEST_NO_TAG(\"Dispatch RTCP packet with referred SSRC to an assigned session\",\n                dispatch_rtcp_packet_with_referred_ssrc_assigned),\n    TEST_NO_TAG(\"Dispatch RTCP packet with referred SSRC to an unassigned session\",\n                dispatch_rtcp_packet_with_referred_ssrc_unassigned),\n    TEST_NO_TAG(\"Dispatch new SSRC with incoming callback set\", dispatch_new_ssrc_with_incoming_callback_set),\n    TEST_NO_TAG(\"Look for outgoing session\", look_out_for_outgoing_session),\n    TEST_NO_TAG(\"Look for outgoing session with outgoing callback set\",\n                look_out_for_outgoing_session_with_outgoing_callback_set),\n};\n\ntest_suite_t bundle_test_suite = {\n    \"Bundle\",         // Name of test suite\n    nullptr,          // Before all callback\n    nullptr,          // After all callback\n    nullptr,          // Before each callback\n    nullptr,          // After each callback\n    std::size(tests), // Size of test table\n    tests,            // Table of test suite\n    0                 // Average execution time\n};\n"
  },
  {
    "path": "tester/extension_header_tester.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP.\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <bctoolbox/defs.h>\n\n#include \"ortp_tester.h\"\n#include <ortp/ortp.h>\n\nstatic RtpSession *session = NULL;\nstatic RtpSession *bundled_session = NULL;\nstatic RtpSession *csrc_session = NULL;\nstatic RtpSession *csrc_bundled_session = NULL;\nstatic RtpBundle *bundle = NULL;\nstatic RtpBundle *csrc_bundle = NULL;\nstatic char mid[9] = \"bundleid\";\nstatic uint32_t CSRC = 0xaa55a55a;\n#define PAYLOAD_SIZE 33\nstatic uint8_t payload[PAYLOAD_SIZE] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,\n                                        0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,\n                                        0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20};\n\nstatic int tester_before_all(void) {\n\tortp_init();\n\tortp_scheduler_init();\n\n\tsession = rtp_session_new(RTP_SESSION_SENDONLY);\n\n\t/* create a session and bundle with id */\n\tbundled_session = rtp_session_new(RTP_SESSION_SENDONLY);\n\tbundle = rtp_bundle_new();\n\trtp_bundle_add_session(bundle, mid, bundled_session);\n\trtp_bundle_set_mid_extension_id(bundle, RTP_EXTENSION_MID);\n\n\t/* create a session with a CSRC */\n\tcsrc_session = rtp_session_new(RTP_SESSION_SENDONLY);\n\trtp_session_add_contributing_source(csrc_session, CSRC, \"cname\", \"name\", \"email\", \"phone\", \"location\", \"tool\",\n\t                                    \"note\");\n\n\t/* create a session with CSRC and bundle it */\n\tcsrc_bundled_session = rtp_session_new(RTP_SESSION_SENDONLY);\n\tcsrc_bundle = rtp_bundle_new();\n\trtp_bundle_add_session(csrc_bundle, mid, csrc_bundled_session);\n\trtp_bundle_set_mid_extension_id(csrc_bundle, RTP_EXTENSION_MID);\n\trtp_session_add_contributing_source(csrc_bundled_session, CSRC, \"cname\", \"name\", \"email\", \"phone\", \"location\",\n\t                                    \"tool\", \"note\");\n\n\treturn 0;\n}\n\nstatic int tester_after_all(void) {\n\trtp_session_destroy(session);\n\trtp_bundle_delete(bundle);\n\tbundle = NULL;\n\trtp_bundle_delete(csrc_bundle);\n\tcsrc_bundle = NULL;\n\trtp_session_destroy(bundled_session);\n\trtp_session_destroy(csrc_session);\n\trtp_session_destroy(csrc_bundled_session);\n\tsession = NULL;\n\tbundled_session = NULL;\n\tcsrc_session = NULL;\n\tcsrc_bundled_session = NULL;\n\n\tortp_exit();\n\n\treturn 0;\n}\n\n/* reset mid sent infos as mid is not sent every packet */\nstatic void tester_before_each(void) {\n\tsession->mid_sent = 0;\n\tsession->last_mid_sent_time = 0;\n\n\tbundled_session->mid_sent = 0;\n\tbundled_session->last_mid_sent_time = 0;\n\n\tcsrc_session->mid_sent = 0;\n\tcsrc_session->last_mid_sent_time = 0;\n\n\tcsrc_bundled_session->mid_sent = 0;\n\tcsrc_bundled_session->last_mid_sent_time = 0;\n}\n\n#define NO_PAYLOAD 0\n#define PAYLOAD 1\n#define DETACHED_PAYLOAD 2\n\nstatic void insert_extension_header_into_packet_base(uint8_t with_payload, RtpSession *test_session) {\n\tuint16_t extbit;\n\tsize_t size, expected_size;\n\tchar *test = \"Running test\";\n\n\tmblk_t *packet;\n\tswitch (with_payload) {\n\t\tcase PAYLOAD:\n\t\t\t/* payload and header in one continuous message block */\n\t\t\tpacket = rtp_session_create_packet_header(\n\t\t\t    test_session, PAYLOAD_SIZE); // ask for PAYLOAD size to be allocated after the header\n\t\t\tmemcpy(packet->b_wptr, payload, PAYLOAD_SIZE);\n\t\t\tpacket->b_wptr += PAYLOAD_SIZE;\n\t\t\tbreak;\n\t\tcase DETACHED_PAYLOAD:\n\t\t\t/* fragmented message block */\n\t\t\tpacket = rtp_session_create_packet_header(test_session, 0);\n\t\t\tpacket->b_cont = rtp_create_packet(payload, PAYLOAD_SIZE);\n\t\t\tbreak;\n\t\tcase NO_PAYLOAD:\n\t\tdefault: // NO PAYLAOD\n\t\t\tpacket = rtp_session_create_packet_header(test_session, 0);\n\t}\n\n\trtp_add_extension_header(packet, 10, strlen(test), (uint8_t *)test);\n\n\textbit = rtp_get_extbit(packet);\n\tBC_ASSERT_EQUAL(extbit, 1, uint16_t, \"%d\");\n\n\tsize = rtp_get_extheader(packet, NULL, NULL);\n\n\texpected_size = (strlen(test) + 1); // extension + 1-byte header + potential padding\n\tif (test_session == bundled_session || test_session == csrc_bundled_session) {\n\t\texpected_size += strlen(mid) + 1;\n\t}\n\tif (expected_size % 4 != 0) {\n\t\texpected_size = expected_size + (4 - expected_size % 4);\n\t}\n\n\tBC_ASSERT_EQUAL(size, expected_size, size_t, \"%zu\");\n\tif (with_payload != NO_PAYLOAD) {\n\t\tuint8_t *p = NULL;\n\t\tint psize = rtp_get_payload(packet, &p);\n\t\tif (BC_ASSERT_TRUE(psize == PAYLOAD_SIZE)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(p, payload, PAYLOAD_SIZE) == 0);\n\t\t}\n\t}\n\n\tif (test_session == bundled_session || test_session == csrc_bundled_session) {\n\t\tuint8_t *data = NULL;\n\t\t/* check the session id is in the header */\n\t\tint size = rtp_get_extension_header(packet, RTP_EXTENSION_MID, &data);\n\t\tBC_ASSERT_EQUAL(size, (int)(strlen(mid)), int, \"%d\");\n\t\tif (size == (int)strlen(mid)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(data, mid, size) == 0);\n\t\t}\n\t}\n\n\tif (test_session == csrc_session || test_session == csrc_bundled_session) {\n\t\tuint16_t cc = rtp_get_cc(packet);\n\t\tBC_ASSERT_EQUAL(cc, 1, uint16_t, \"%d\");\n\t\tif (cc == 1) {\n\t\t\tuint32_t csrc = rtp_get_csrc(packet, 0);\n\t\t\tBC_ASSERT_EQUAL(csrc, CSRC, uint32_t, \"%d\");\n\t\t}\n\t}\n\n\tfreemsg(packet);\n}\n\nstatic void insert_extension_header_into_packet(void) {\n\tinsert_extension_header_into_packet_base(NO_PAYLOAD, session);\n}\n\nstatic void insert_extension_header_into_packet_with_payload(void) {\n\tinsert_extension_header_into_packet_base(PAYLOAD, session);\n}\n\nstatic void insert_extension_header_into_packet_with_detached_payload(void) {\n\tinsert_extension_header_into_packet_base(DETACHED_PAYLOAD, session);\n}\n\nstatic void insert_extension_header_into_packet_in_bundled_session(void) {\n\tinsert_extension_header_into_packet_base(NO_PAYLOAD, bundled_session);\n}\nstatic void insert_extension_header_into_packet_with_payload_in_bundled_session(void) {\n\tinsert_extension_header_into_packet_base(PAYLOAD, bundled_session);\n}\nstatic void insert_extension_header_into_packet_with_detached_payload_in_bundled_session(void) {\n\tinsert_extension_header_into_packet_base(DETACHED_PAYLOAD, bundled_session);\n}\n\nstatic void insert_extension_header_into_packet_in_csrc_session(void) {\n\tinsert_extension_header_into_packet_base(NO_PAYLOAD, csrc_session);\n}\nstatic void insert_extension_header_into_packet_with_payload_in_csrc_session(void) {\n\tinsert_extension_header_into_packet_base(PAYLOAD, csrc_session);\n}\nstatic void insert_extension_header_into_packet_with_detached_payload_in_csrc_session(void) {\n\tinsert_extension_header_into_packet_base(DETACHED_PAYLOAD, csrc_session);\n}\n\nstatic void insert_extension_header_into_packet_in_csrc_bundled_session(void) {\n\tinsert_extension_header_into_packet_base(NO_PAYLOAD, csrc_bundled_session);\n}\nstatic void insert_extension_header_into_packet_with_payload_in_csrc_bundled_session(void) {\n\tinsert_extension_header_into_packet_base(PAYLOAD, csrc_bundled_session);\n}\nstatic void insert_extension_header_into_packet_with_detached_payload_in_csrc_bundled_session(void) {\n\tinsert_extension_header_into_packet_base(DETACHED_PAYLOAD, csrc_bundled_session);\n}\n\nstatic void insert_multiple_extension_headers_into_packet_base(uint8_t with_payload, RtpSession *test_session) {\n\tint i;\n\tmblk_t *packet;\n\tchar *test = \"running test\"; // 12 bytes -> 13 with header, 1 padding bytes\n\tchar *foo = \"foo\";           // -> no padding bytes\n\tchar *bar = \"bar12\";         // -> 2 padding bytes\n\tint expected_header_size = 0;\n\tint expected_header_size_with_padding = 0;\n\tuint8_t expected_extensions_values[10][64];\n\tint expected_extensions_size[10];\n\n\t// Test multiple extension into the same packet\n\tswitch (with_payload) {\n\t\tcase PAYLOAD:\n\t\t\t/* payload and header in one continuous message block */\n\t\t\tpacket = rtp_session_create_packet_header(\n\t\t\t    test_session, PAYLOAD_SIZE); // ask for PAYLOAD size to be allocated after the header\n\t\t\tmemcpy(packet->b_wptr, payload, PAYLOAD_SIZE);\n\t\t\tpacket->b_wptr += PAYLOAD_SIZE;\n\t\t\tbreak;\n\t\tcase DETACHED_PAYLOAD:\n\t\t\t/* fragmented message block */\n\t\t\tpacket = rtp_session_create_packet_header(test_session, 0);\n\t\t\tpacket->b_cont = rtp_create_packet(payload, PAYLOAD_SIZE);\n\t\t\tbreak;\n\t\tcase NO_PAYLOAD:\n\t\tdefault: // NO PAYLAOD\n\t\t\tpacket = rtp_session_create_packet_header(test_session, 0);\n\t}\n\n\tif (test_session == bundled_session || test_session == csrc_bundled_session) {\n\t\texpected_header_size += strlen(mid) + 1;\n\t}\n\n\tfor (i = 1; i < 11; i++) {\n\t\tif ((test_session == bundled_session || test_session == csrc_bundled_session) && i == RTP_EXTENSION_MID)\n\t\t\tcontinue; // Do no overwritte MID header if any\n\t\tswitch ((bctbx_random() % 3) + 1) {\n\t\t\tcase 1:\n\t\t\t\trtp_add_extension_header(packet, i, strlen(test), (uint8_t *)test);\n\t\t\t\texpected_header_size += 1 + strlen(test); // 1 byte header plus extension itself\n\t\t\t\texpected_extensions_size[i - 1] = strlen(test);\n\t\t\t\tmemcpy(expected_extensions_values[i - 1], test, strlen(test));\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\trtp_add_extension_header(packet, i, strlen(foo), (uint8_t *)foo);\n\t\t\t\texpected_header_size += 1 + strlen(foo); // 1 byte header plus extension itself\n\t\t\t\texpected_extensions_size[i - 1] = strlen(foo);\n\t\t\t\tmemcpy(expected_extensions_values[i - 1], foo, strlen(foo));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\trtp_add_extension_header(packet, i, strlen(bar), (uint8_t *)bar);\n\t\t\t\texpected_header_size += 1 + strlen(bar); // 1 byte header plus extension itself\n\t\t\t\texpected_extensions_size[i - 1] = strlen(bar);\n\t\t\t\tmemcpy(expected_extensions_values[i - 1], bar, strlen(bar));\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (expected_header_size % 4 != 0) {\n\t\t\texpected_header_size_with_padding = expected_header_size + (4 - expected_header_size % 4);\n\t\t} else {\n\t\t\texpected_header_size_with_padding = expected_header_size;\n\t\t}\n\t\tBC_ASSERT_EQUAL(rtp_get_extheader(packet, NULL, NULL), expected_header_size_with_padding, int, \"%d\");\n\t}\n\n\tfor (i = 0; i < 10; i++) {\n\t\tuint8_t *data = NULL;\n\t\tint size = rtp_get_extension_header(packet, i + 1, &data);\n\t\tif ((test_session == bundled_session || test_session == csrc_bundled_session) && i + 1 == RTP_EXTENSION_MID) {\n\t\t\tBC_ASSERT_EQUAL(size, (int)(strlen(mid)), int, \"%d\");\n\t\t\tif (size == (int)strlen(mid)) {\n\t\t\t\tBC_ASSERT_TRUE(memcmp(data, mid, size) == 0);\n\t\t\t}\n\t\t} else {\n\t\t\tBC_ASSERT_EQUAL(size, expected_extensions_size[i], int, \"%d\");\n\t\t\tif (size == expected_extensions_size[i]) {\n\t\t\t\tBC_ASSERT_TRUE(memcmp(data, expected_extensions_values[i], size) == 0);\n\t\t\t}\n\t\t}\n\t}\n\tif (with_payload == TRUE) {\n\t\tuint8_t *p = NULL;\n\t\tint psize = rtp_get_payload(packet, &p);\n\t\tif (BC_ASSERT_TRUE(psize == PAYLOAD_SIZE)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(p, payload, PAYLOAD_SIZE) == 0);\n\t\t}\n\t}\n\n\tif (test_session == csrc_session || test_session == csrc_bundled_session) {\n\t\tuint16_t cc = rtp_get_cc(packet);\n\t\tBC_ASSERT_EQUAL(cc, 1, uint16_t, \"%d\");\n\t\tif (cc == 1) {\n\t\t\tuint32_t csrc = rtp_get_csrc(packet, 0);\n\t\t\tBC_ASSERT_EQUAL(csrc, CSRC, uint32_t, \"%d\");\n\t\t}\n\t}\n\n\tfreemsg(packet);\n}\n\nstatic void insert_multiple_extension_headers_into_packet(void) {\n\tinsert_multiple_extension_headers_into_packet_base(NO_PAYLOAD, session);\n}\nstatic void insert_multiple_extension_headers_into_packet_with_payload(void) {\n\tinsert_multiple_extension_headers_into_packet_base(PAYLOAD, session);\n}\nstatic void insert_multiple_extension_headers_into_packet_with_detached_payload(void) {\n\tinsert_multiple_extension_headers_into_packet_base(DETACHED_PAYLOAD, session);\n}\n\nstatic void insert_multiple_extension_headers_into_packet_in_bundled_session(void) {\n\tinsert_multiple_extension_headers_into_packet_base(NO_PAYLOAD, bundled_session);\n}\nstatic void insert_multiple_extension_headers_into_packet_with_payload_in_bundled_session(void) {\n\tinsert_multiple_extension_headers_into_packet_base(PAYLOAD, bundled_session);\n}\nstatic void insert_multiple_extension_headers_into_packet_with_detached_payload_in_bundled_session(void) {\n\tinsert_multiple_extension_headers_into_packet_base(DETACHED_PAYLOAD, bundled_session);\n}\n\nstatic void insert_multiple_extension_headers_into_packet_in_csrc_session(void) {\n\tinsert_multiple_extension_headers_into_packet_base(NO_PAYLOAD, csrc_session);\n}\nstatic void insert_multiple_extension_headers_into_packet_with_payload_in_csrc_session(void) {\n\tinsert_multiple_extension_headers_into_packet_base(PAYLOAD, csrc_session);\n}\nstatic void insert_multiple_extension_headers_into_packet_with_detached_payload_in_csrc_session(void) {\n\tinsert_multiple_extension_headers_into_packet_base(DETACHED_PAYLOAD, csrc_session);\n}\n\nstatic void insert_multiple_extension_headers_into_packet_in_csrc_bundled_session(void) {\n\tinsert_multiple_extension_headers_into_packet_base(NO_PAYLOAD, csrc_bundled_session);\n}\nstatic void insert_multiple_extension_headers_into_packet_with_payload_in_csrc_bundled_session(void) {\n\tinsert_multiple_extension_headers_into_packet_base(PAYLOAD, csrc_bundled_session);\n}\nstatic void insert_multiple_extension_headers_into_packet_with_detached_payload_in_csrc_bundled_session(void) {\n\tinsert_multiple_extension_headers_into_packet_base(DETACHED_PAYLOAD, csrc_bundled_session);\n}\n\nstatic void insert_client_to_mixer_into_packet_base(uint8_t with_payload, RtpSession *test_session) {\n\tint result;\n\tbool_t voice_activity;\n\tmblk_t *packet = NULL;\n\n\tswitch (with_payload) {\n\t\tcase PAYLOAD:\n\t\t\t/* payload and header in one continuous message block */\n\t\t\tpacket = rtp_session_create_packet_header(\n\t\t\t    test_session, PAYLOAD_SIZE); // ask for PAYLOAD size to be allocated after the header\n\t\t\tmemcpy(packet->b_wptr, payload, PAYLOAD_SIZE);\n\t\t\tpacket->b_wptr += PAYLOAD_SIZE;\n\t\t\tbreak;\n\t\tcase DETACHED_PAYLOAD:\n\t\t\t/* fragmented message block */\n\t\t\tpacket = rtp_session_create_packet_header(test_session, 0);\n\t\t\tpacket->b_cont = rtp_create_packet(payload, PAYLOAD_SIZE);\n\t\t\tbreak;\n\t\tcase NO_PAYLOAD:\n\t\tdefault: // NO PAYLAOD\n\t\t\tpacket = rtp_session_create_packet_header(test_session, 0);\n\t}\n\n\trtp_add_client_to_mixer_audio_level(packet, RTP_EXTENSION_CLIENT_TO_MIXER_AUDIO_LEVEL, TRUE, -64);\n\n\tresult = rtp_get_client_to_mixer_audio_level(packet, RTP_EXTENSION_CLIENT_TO_MIXER_AUDIO_LEVEL, &voice_activity);\n\tBC_ASSERT_EQUAL(result, -64, int, \"%d\");\n\tBC_ASSERT_EQUAL(voice_activity, TRUE, bool_t, \"%d\");\n\trtp_add_client_to_mixer_audio_level(packet, RTP_EXTENSION_CLIENT_TO_MIXER_AUDIO_LEVEL + 1, FALSE, 0);\n\tresult = rtp_get_client_to_mixer_audio_level(packet, RTP_EXTENSION_CLIENT_TO_MIXER_AUDIO_LEVEL, &voice_activity);\n\tBC_ASSERT_EQUAL(result, -64, int, \"%d\");\n\tBC_ASSERT_EQUAL(voice_activity, TRUE, int, \"%d\");\n\tresult =\n\t    rtp_get_client_to_mixer_audio_level(packet, RTP_EXTENSION_CLIENT_TO_MIXER_AUDIO_LEVEL + 1, &voice_activity);\n\tBC_ASSERT_EQUAL(result, 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(voice_activity, FALSE, int, \"%d\");\n\n\tif (with_payload == TRUE) {\n\t\tuint8_t *p = NULL;\n\t\tint psize = rtp_get_payload(packet, &p);\n\t\tif (BC_ASSERT_TRUE(psize == PAYLOAD_SIZE)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(p, payload, PAYLOAD_SIZE) == 0);\n\t\t}\n\t}\n\n\tif (test_session == bundled_session || test_session == csrc_bundled_session) {\n\t\tuint8_t *data = NULL;\n\t\t/* check the session id is in the header */\n\t\tint size = rtp_get_extension_header(packet, RTP_EXTENSION_MID, &data);\n\t\tBC_ASSERT_EQUAL(size, (int)(strlen(mid)), int, \"%d\");\n\t\tif (size == (int)strlen(mid)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(data, mid, size) == 0);\n\t\t}\n\t}\n\n\tif (test_session == csrc_session || test_session == csrc_bundled_session) {\n\t\tuint16_t cc = rtp_get_cc(packet);\n\t\tBC_ASSERT_EQUAL(cc, 1, uint16_t, \"%d\");\n\t\tif (cc == 1) {\n\t\t\tuint32_t csrc = rtp_get_csrc(packet, 0);\n\t\t\tBC_ASSERT_EQUAL(csrc, CSRC, uint32_t, \"%d\");\n\t\t}\n\t}\n\n\tfreemsg(packet);\n}\n\nstatic void insert_client_to_mixer_into_packet(void) {\n\tinsert_client_to_mixer_into_packet_base(NO_PAYLOAD, session);\n}\nstatic void insert_client_to_mixer_into_packet_with_payload(void) {\n\tinsert_client_to_mixer_into_packet_base(PAYLOAD, session);\n}\nstatic void insert_client_to_mixer_into_packet_with_detached_payload(void) {\n\tinsert_client_to_mixer_into_packet_base(DETACHED_PAYLOAD, session);\n}\n\nstatic void insert_client_to_mixer_into_packet_in_bundled_session(void) {\n\tinsert_client_to_mixer_into_packet_base(NO_PAYLOAD, bundled_session);\n}\nstatic void insert_client_to_mixer_into_packet_with_payload_in_bundled_session(void) {\n\tinsert_client_to_mixer_into_packet_base(PAYLOAD, bundled_session);\n}\nstatic void insert_client_to_mixer_into_packet_with_detached_payload_in_bundled_session(void) {\n\tinsert_client_to_mixer_into_packet_base(DETACHED_PAYLOAD, bundled_session);\n}\n\nstatic void insert_client_to_mixer_into_packet_in_csrc_session(void) {\n\tinsert_client_to_mixer_into_packet_base(NO_PAYLOAD, csrc_session);\n}\nstatic void insert_client_to_mixer_into_packet_with_payload_in_csrc_session(void) {\n\tinsert_client_to_mixer_into_packet_base(PAYLOAD, csrc_session);\n}\nstatic void insert_client_to_mixer_into_packet_with_detached_payload_in_csrc_session(void) {\n\tinsert_client_to_mixer_into_packet_base(DETACHED_PAYLOAD, csrc_session);\n}\n\nstatic void insert_client_to_mixer_into_packet_in_csrc_bundled_session(void) {\n\tinsert_client_to_mixer_into_packet_base(NO_PAYLOAD, csrc_bundled_session);\n}\nstatic void insert_client_to_mixer_into_packet_with_payload_in_csrc_bundled_session(void) {\n\tinsert_client_to_mixer_into_packet_base(PAYLOAD, csrc_bundled_session);\n}\nstatic void insert_client_to_mixer_into_packet_with_detached_payload_in_csrc_bundled_session(void) {\n\tinsert_client_to_mixer_into_packet_base(DETACHED_PAYLOAD, csrc_bundled_session);\n}\n\nstatic void\ninsert_mixer_to_client_into_packet_base(bool_t with_payload, bool_t use_create_with_mixer, RtpSession *test_session) {\n\tint audio_size;\n\trtp_audio_level_t audio_levels[15];\n\trtp_audio_level_t values[5] = {{1, -127}, {2, -115}, {0, -53}, {4, -28}, {5, 0}};\n\tmblk_t *packet = NULL;\n\n\tif (use_create_with_mixer) {\n\t\tpacket = rtp_session_create_packet_header_with_mixer_to_client_audio_level(\n\t\t    test_session, 0, RTP_EXTENSION_MIXER_TO_CLIENT_AUDIO_LEVEL, 5, values);\n\t\tif (with_payload == TRUE) {\n\t\t\tpacket->b_cont = rtp_create_packet(payload, PAYLOAD_SIZE);\n\t\t}\n\t} else {\n\t\tpacket = rtp_session_create_packet_header(test_session, 0);\n\t\tif (with_payload == TRUE) {\n\t\t\tpacket->b_cont = rtp_create_packet(payload, PAYLOAD_SIZE);\n\t\t}\n\t\trtp_add_mixer_to_client_audio_level(packet, RTP_EXTENSION_MIXER_TO_CLIENT_AUDIO_LEVEL, 5, values);\n\t}\n\n\taudio_size = rtp_get_mixer_to_client_audio_level(packet, RTP_EXTENSION_MIXER_TO_CLIENT_AUDIO_LEVEL, audio_levels);\n\n\tBC_ASSERT_EQUAL(audio_size, 5, int, \"%d\");\n\n\tif (audio_size == 5) {\n\t\tBC_ASSERT_EQUAL(audio_levels[0].csrc, 1, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(audio_levels[0].dbov, -127, int, \"%d\");\n\n\t\tBC_ASSERT_EQUAL(audio_levels[1].csrc, 2, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(audio_levels[1].dbov, -115, int, \"%d\");\n\n\t\tBC_ASSERT_EQUAL(audio_levels[2].csrc, 0, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(audio_levels[2].dbov, -53, int, \"%d\");\n\n\t\tBC_ASSERT_EQUAL(audio_levels[3].csrc, 4, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(audio_levels[3].dbov, -28, int, \"%d\");\n\n\t\tBC_ASSERT_EQUAL(audio_levels[4].csrc, 5, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(audio_levels[4].dbov, 0, int, \"%d\");\n\t}\n\n\tif (with_payload == TRUE) {\n\t\tuint8_t *p = NULL;\n\t\tint psize = rtp_get_payload(packet, &p);\n\t\tif (BC_ASSERT_TRUE(psize == PAYLOAD_SIZE)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(p, payload, PAYLOAD_SIZE) == 0);\n\t\t}\n\t}\n\n\tif (test_session == bundled_session) {\n\t\tuint8_t *data = NULL;\n\t\t/* check the session id is in the header */\n\t\tint size = rtp_get_extension_header(packet, RTP_EXTENSION_MID, &data);\n\t\tBC_ASSERT_EQUAL(size, (int)(strlen(mid)), int, \"%d\");\n\t\tif (size == (int)strlen(mid)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(data, mid, size) == 0);\n\t\t}\n\t}\n\n\tfreemsg(packet);\n}\nstatic void insert_mixer_to_client_into_packet(void) {\n\tinsert_mixer_to_client_into_packet_base(FALSE, FALSE, session);\n}\nstatic void insert_mixer_to_client_into_packet_with_payload(void) {\n\tinsert_mixer_to_client_into_packet_base(TRUE, FALSE, session);\n}\nstatic void insert_mixer_to_client_into_packet_in_bundled_session(void) {\n\tinsert_mixer_to_client_into_packet_base(FALSE, FALSE, bundled_session);\n}\nstatic void insert_mixer_to_client_into_packet_with_payload_in_bundled_session(void) {\n\tinsert_mixer_to_client_into_packet_base(TRUE, FALSE, bundled_session);\n}\n\nstatic void insert_mixer_to_client_into_packet_use_create_with_mixer(void) {\n\tinsert_mixer_to_client_into_packet_base(FALSE, TRUE, session);\n}\nstatic void insert_mixer_to_client_into_packet_with_payload_use_create_with_mixer(void) {\n\tinsert_mixer_to_client_into_packet_base(TRUE, TRUE, session);\n}\nstatic void insert_mixer_to_client_into_packet_in_bundled_session_use_create_with_mixer(void) {\n\tinsert_mixer_to_client_into_packet_base(FALSE, TRUE, bundled_session);\n}\nstatic void insert_mixer_to_client_into_packet_with_payload_in_bundled_session_use_create_with_mixer(void) {\n\tinsert_mixer_to_client_into_packet_base(TRUE, TRUE, bundled_session);\n}\n\nstatic void insert_frame_marking_into_packet_base(BCTBX_UNUSED(bool_t with_payload),\n                                                  BCTBX_UNUSED(RtpSession *test_session)) {\n\tsize_t size;\n\tint ret;\n\tuint8_t result;\n\n\tmblk_t *packet = rtp_session_create_packet_header(session, 0);\n\tuint8_t marker = RTP_FRAME_MARKER_START | RTP_FRAME_MARKER_INDEPENDENT;\n\n\trtp_add_frame_marker(packet, RTP_EXTENSION_FRAME_MARKING, marker);\n\n\tsize = rtp_get_extheader(packet, NULL, NULL);\n\tBC_ASSERT_GREATER(size, 0, size_t, \"%zu\");\n\n\tret = rtp_get_frame_marker(packet, RTP_EXTENSION_FRAME_MARKING, &result);\n\tBC_ASSERT_EQUAL(ret, 1, int, \"%d\");\n\tBC_ASSERT_TRUE(result & RTP_FRAME_MARKER_START);\n\tBC_ASSERT_TRUE(result & RTP_FRAME_MARKER_INDEPENDENT);\n\n\tfreemsg(packet);\n}\nstatic void insert_frame_marking_into_packet(void) {\n\tinsert_frame_marking_into_packet_base(FALSE, session);\n}\nstatic void insert_frame_marking_into_packet_with_payload(void) {\n\tinsert_frame_marking_into_packet_base(TRUE, session);\n}\nstatic void insert_frame_marking_into_packet_in_bundled_session(void) {\n\tinsert_frame_marking_into_packet_base(FALSE, bundled_session);\n}\nstatic void insert_frame_marking_into_packet_with_payload_in_bundled_session(void) {\n\tinsert_frame_marking_into_packet_base(TRUE, bundled_session);\n}\n\nstatic void padding_test(void) {\n\t// packet with the header, ext are 1 : bar1, 2:foo, 3 padding bytes\n\tuint8_t ext1[4] = {0x62, 0x61, 0x72, 0x31};       // extension with id 1 is \"bar1\"\n\tuint8_t ext2[3] = {0x66, 0x6f, 0x6f};             // extension with id 2 is \"foo\"\n\tuint8_t ext3[5] = {0x01, 0x02, 0x03, 0x04, 0x05}; // extension with id 3\n\tuint8_t raw_packet_padding_at_the_end[28] = {0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x8a,\n\t                                             0x1a, 0x76, 0xbe, 0xde, 0x00, 0x03, 0x13, 0x62, 0x61, 0x72,\n\t                                             0x31, 0x22, 0x66, 0x6f, 0x6f, 0x00, 0x00, 0x00};\n\tuint8_t raw_packet_padding_at_the_begining[28] = {0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x8a,\n\t                                                  0x1a, 0x76, 0xbe, 0xde, 0x00, 0x03, 0x00, 0x00, 0x00, 0x13,\n\t                                                  0x62, 0x61, 0x72, 0x31, 0x22, 0x66, 0x6f, 0x6f};\n\tuint8_t raw_packet_padding_in_the_middle[28] = {0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x8a,\n\t                                                0x1a, 0x76, 0xbe, 0xde, 0x00, 0x03, 0x13, 0x62, 0x61, 0x72,\n\t                                                0x31, 0x00, 0x00, 0x00, 0x22, 0x66, 0x6f, 0x6f};\n\tuint8_t *raw_packets[3] = {raw_packet_padding_at_the_end, raw_packet_padding_at_the_begining,\n\t                           raw_packet_padding_in_the_middle};\n\tmblk_t *packet;\n\tuint8_t *data;\n\n\tint i, size;\n\n\tfor (i = 0; i < 3; i++) {\n\t\tortp_message(\"run on pattern %d\", i);\n\t\tpacket = rtp_create_packet(raw_packets[i], 28);\n\t\t/* check ext bit and size - expected to be 12 */\n\t\tBC_ASSERT_EQUAL(rtp_get_extbit(packet), 1, uint16_t, \"%d\");\n\t\tsize = rtp_get_extheader(packet, NULL, NULL);\n\t\tBC_ASSERT_EQUAL(size, 12, unsigned int, \"%d\");\n\n\t\t/* check ext 1*/\n\t\tsize = rtp_get_extension_header(packet, 1, &data);\n\t\tBC_ASSERT_EQUAL(size, sizeof(ext1), int, \"%d\");\n\t\tif (size == sizeof(ext1)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(data, ext1, size) == 0);\n\t\t}\n\n\t\t/* check ext 2*/\n\t\tsize = rtp_get_extension_header(packet, 2, &data);\n\t\tBC_ASSERT_EQUAL(size, sizeof(ext2), int, \"%d\");\n\t\tif (size == sizeof(ext2)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(data, ext2, size) == 0);\n\t\t}\n\n\t\t/* add a new header ext */\n\t\trtp_add_extension_header(packet, 3, sizeof(ext3), ext3);\n\n\t\t/* check ext bit and size - expected to be 16 */\n\t\tBC_ASSERT_EQUAL(rtp_get_extbit(packet), 1, uint16_t, \"%d\");\n\t\tsize = rtp_get_extheader(packet, NULL, NULL);\n\t\tif (i == 0) {\n\t\t\t// when padding is at the end, it is re-used, so we can fit the 3 extensions in 16 bytes\n\t\t\t// 4+3+5 for content, 3 for ids = 15 bytes -> 16 bytes with rounding up to %4=0\n\t\t\tBC_ASSERT_EQUAL(size, 16, unsigned int, \"%d\");\n\t\t} else {\n\t\t\t// when padding is not at the end, we will not reuse it when adding an extension. We need 20 bytes to fit\n\t\t\t// the 3 extensions 4+3+5 for content, 3 for ids + 3 padding bytes from the original message = 18 -> 20\n\t\t\t// bytes with rounding up to %4=0\n\t\t\tBC_ASSERT_EQUAL(size, 20, unsigned int, \"%d\");\n\t\t}\n\n\t\t/* check ext 1*/\n\t\tsize = rtp_get_extension_header(packet, 1, &data);\n\t\tBC_ASSERT_EQUAL(size, sizeof(ext1), int, \"%d\");\n\t\tif (size == sizeof(ext1)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(data, ext1, size) == 0);\n\t\t}\n\n\t\t/* check ext 2*/\n\t\tsize = rtp_get_extension_header(packet, 2, &data);\n\t\tBC_ASSERT_EQUAL(size, sizeof(ext2), int, \"%d\");\n\t\tif (size == sizeof(ext2)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(data, ext2, size) == 0);\n\t\t}\n\n\t\t/* check ext 3*/\n\t\tsize = rtp_get_extension_header(packet, 3, &data);\n\t\tBC_ASSERT_EQUAL(size, sizeof(ext3), int, \"%d\");\n\t\tif (size == sizeof(ext3)) {\n\t\t\tBC_ASSERT_TRUE(memcmp(data, ext3, size) == 0);\n\t\t}\n\n\t\tfreemsg(packet);\n\t}\n}\n\nstatic void create_packet_with_payload_in_bundled_session(void) {\n\tuint8_t *data;\n\tint size = 0;\n\tmblk_t *packet = NULL;\n\n\t/* create a packet in the bundled session, without payload */\n\tpacket = rtp_session_create_packet_header(bundled_session, 0);\n\t/* check the session id is in the header */\n\tsize = rtp_get_extension_header(packet, RTP_EXTENSION_MID, &data);\n\tBC_ASSERT_EQUAL(size, (int)(strlen(mid)), int, \"%d\");\n\tif (size == (int)(strlen(mid))) {\n\t\tBC_ASSERT_TRUE(memcmp(data, mid, size) == 0);\n\t}\n\tfreemsg(packet);\n\n\t/* same but with a payload in a non fragmented message block */\n\tpacket = rtp_session_create_packet_header(bundled_session, PAYLOAD_SIZE);\n\tmemcpy(packet->b_wptr, payload, PAYLOAD_SIZE);\n\tpacket->b_wptr += PAYLOAD_SIZE;\n\t/* check the session id is in the header */\n\tsize = rtp_get_extension_header(packet, RTP_EXTENSION_MID, &data);\n\tBC_ASSERT_EQUAL(size, (int)(strlen(mid)), int, \"%d\");\n\tif (size == (int)(strlen(mid))) {\n\t\tBC_ASSERT_TRUE(memcmp(data, mid, size) == 0);\n\t}\n\tsize = rtp_get_payload(packet, &data);\n\tif (BC_ASSERT_TRUE(size == PAYLOAD_SIZE)) {\n\t\tBC_ASSERT_TRUE(memcmp(data, payload, PAYLOAD_SIZE) == 0);\n\t}\n\tfreemsg(packet);\n\n\t/* same but with a payload in a fragmented message block */\n\tpacket = rtp_session_create_packet_header(bundled_session, 0);\n\tpacket->b_cont = rtp_create_packet(payload, PAYLOAD_SIZE);\n\t/* check the session id is in the header */\n\tsize = rtp_get_extension_header(packet, RTP_EXTENSION_MID, &data);\n\tBC_ASSERT_EQUAL(size, (int)(strlen(mid)), int, \"%d\");\n\tif (size == (int)(strlen(mid))) {\n\t\tBC_ASSERT_TRUE(memcmp(data, mid, size) == 0);\n\t}\n\tsize = rtp_get_payload(packet, &data);\n\tif (BC_ASSERT_TRUE(size == PAYLOAD_SIZE)) {\n\t\tBC_ASSERT_TRUE(memcmp(data, payload, PAYLOAD_SIZE) == 0);\n\t}\n\tfreemsg(packet);\n}\n\nstatic void remap_extension_header_ids_from_packet(void) {\n\tsize_t size;\n\tint ret;\n\tuint8_t result;\n\n\tmblk_t *packet = rtp_session_create_packet_header(session, 0);\n\n\t// Add multiple extensions with default IDs\n\tconst char *mid = \"as\";\n\trtp_add_extension_header(packet, RTP_EXTENSION_MID, strlen(mid), (uint8_t *)mid);\n\n\tuint8_t marker = RTP_FRAME_MARKER_START | RTP_FRAME_MARKER_INDEPENDENT;\n\trtp_add_frame_marker(packet, RTP_EXTENSION_FRAME_MARKING, marker);\n\n\trtp_add_client_to_mixer_audio_level(packet, RTP_EXTENSION_CLIENT_TO_MIXER_AUDIO_LEVEL, TRUE, -127);\n\n\t// Remap IDs\n\tint mapping[16] = {0};\n\tmapping[RTP_EXTENSION_MID] = 4;\n\tmapping[RTP_EXTENSION_FRAME_MARKING] = 8;\n\n\trtp_remap_header_extension_ids(packet, mapping);\n\n\t// Verify that all extensions are correct with updated ID except client to mixer since it wasn't remaped\n\tsize = rtp_get_extheader(packet, NULL, NULL);\n\tBC_ASSERT_GREATER(size, 0, size_t, \"%zu\");\n\n\tuint8_t *data = NULL;\n\tret = rtp_get_extension_header(packet, 4, &data);\n\tBC_ASSERT_EQUAL(ret, (int)(strlen(mid)), int, \"%d\");\n\tif (ret == (int)strlen(mid)) {\n\t\tBC_ASSERT_TRUE(memcmp(data, mid, ret) == 0);\n\t}\n\n\tret = rtp_get_frame_marker(packet, 8, &result);\n\tBC_ASSERT_EQUAL(ret, 1, int, \"%d\");\n\tBC_ASSERT_TRUE(result & RTP_FRAME_MARKER_START);\n\tBC_ASSERT_TRUE(result & RTP_FRAME_MARKER_INDEPENDENT);\n\n\tbool_t voice_activity = FALSE;\n\tret = rtp_get_client_to_mixer_audio_level(packet, RTP_EXTENSION_CLIENT_TO_MIXER_AUDIO_LEVEL, &voice_activity);\n\tBC_ASSERT_EQUAL(ret, -127, int, \"%d\");\n\tBC_ASSERT_TRUE(voice_activity);\n\n\tfreemsg(packet);\n}\n\nstatic void add_existing_extensions_to_packet(void) {\n\tsize_t size;\n\tint ret;\n\tuint8_t *data = NULL;\n\n\t// Create a packet\n\tmblk_t *packet = rtp_session_create_packet_header(session, 0);\n\n\t// Add the first extension (MID)\n\tconst char *mid = \"first\";\n\trtp_add_extension_header(packet, RTP_EXTENSION_MID, strlen(mid), (uint8_t *)mid);\n\n\t// Add another extension (FRAME_MARKING)\n\tuint8_t marker = RTP_FRAME_MARKER_START | RTP_FRAME_MARKER_INDEPENDENT;\n\trtp_add_frame_marker(packet, RTP_EXTENSION_FRAME_MARKING, marker);\n\n\t// Try to add another MID with the same extension ID again\n\tconst char *mid2 = \"second\";\n\trtp_add_extension_header(packet, RTP_EXTENSION_MID, strlen(mid2), (uint8_t *)mid2);\n\n\t// Verify the extensions were added properly\n\tsize = rtp_get_extheader(packet, NULL, NULL);\n\tBC_ASSERT_GREATER(size, 0, size_t, \"%zu\");\n\n\t// Verify the MID extension\n\tret = rtp_get_extension_header(packet, RTP_EXTENSION_MID, &data);\n\tBC_ASSERT_EQUAL(ret, (int)(strlen(mid2)), int, \"%d\");\n\tif (ret == (int)strlen(mid)) {\n\t\tBC_ASSERT_TRUE(memcmp(data, mid2, ret) == 0);\n\t}\n\n\t// Verify the FRAME_MARKING extension\n\tuint8_t result;\n\tret = rtp_get_frame_marker(packet, RTP_EXTENSION_FRAME_MARKING, &result);\n\tBC_ASSERT_EQUAL(ret, 1, int, \"%d\");\n\tBC_ASSERT_TRUE(result & RTP_FRAME_MARKER_START);\n\tBC_ASSERT_TRUE(result & RTP_FRAME_MARKER_INDEPENDENT);\n\n\t// Verify that adding the same MID extension does not create duplicates\n\tint count = 0;\n\tsize = rtp_get_extheader(packet, NULL, &data);\n\n\tuint8_t *tmp = data;\n\twhile (tmp < data + size) {\n\t\tif (*tmp == RTP_EXTENSION_NONE) {\n\t\t\ttmp += 1; // Padding\n\t\t} else {\n\t\t\tif (*tmp >> 4 == RTP_EXTENSION_MID) count++;\n\t\t\ttmp += (size_t)(*tmp & 0xF) + 1 + 1; // Length is a 4-bit number minus 1\n\t\t}\n\t}\n\n\tBC_ASSERT_EQUAL(count, 1, int, \"%d\"); // Ensure no duplicates of the same extension were added\n\n\t// Free the packet\n\tfreemsg(packet);\n}\n\nstatic test_t tests[] = {\n    TEST_NO_TAG(\"Create packet with payload in a bundled session\", create_packet_with_payload_in_bundled_session),\n    TEST_NO_TAG(\"Insert an extension header into a packet\", insert_extension_header_into_packet),\n    TEST_NO_TAG(\"Insert an extension header into a packet with payload\",\n                insert_extension_header_into_packet_with_payload),\n    TEST_NO_TAG(\"Insert an extension header into a packet with detached payload\",\n                insert_extension_header_into_packet_with_detached_payload),\n    TEST_NO_TAG(\"Insert an extension header into a packet in bundled session\",\n                insert_extension_header_into_packet_in_bundled_session),\n    TEST_NO_TAG(\"Insert an extension header into a packet with payload in bundled session\",\n                insert_extension_header_into_packet_with_payload_in_bundled_session),\n    TEST_NO_TAG(\"Insert an extension header into a packet with detached payload in bundled session\",\n                insert_extension_header_into_packet_with_detached_payload_in_bundled_session),\n    TEST_NO_TAG(\"Insert an extension header into a packet in csrc session\",\n                insert_extension_header_into_packet_in_csrc_session),\n    TEST_NO_TAG(\"Insert an extension header into a packet with payload in csrc session\",\n                insert_extension_header_into_packet_with_payload_in_csrc_session),\n    TEST_NO_TAG(\"Insert an extension header into a packet with detached payload in csrc session\",\n                insert_extension_header_into_packet_with_detached_payload_in_csrc_session),\n    TEST_NO_TAG(\"Insert an extension header into a packet in csrc bundled session\",\n                insert_extension_header_into_packet_in_csrc_bundled_session),\n    TEST_NO_TAG(\"Insert an extension header into a packet with payload in csrc bundled session\",\n                insert_extension_header_into_packet_with_payload_in_csrc_bundled_session),\n    TEST_NO_TAG(\"Insert an extension header into a packet with detached payload in csrc bundled session\",\n                insert_extension_header_into_packet_with_detached_payload_in_csrc_bundled_session),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet\", insert_multiple_extension_headers_into_packet),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet with payload\",\n                insert_multiple_extension_headers_into_packet_with_payload),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet with detached payload\",\n                insert_multiple_extension_headers_into_packet_with_detached_payload),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet in bundled session\",\n                insert_multiple_extension_headers_into_packet_in_bundled_session),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet with payload in bundled session\",\n                insert_multiple_extension_headers_into_packet_with_payload_in_bundled_session),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet with detached payload in bundled session\",\n                insert_multiple_extension_headers_into_packet_with_detached_payload_in_bundled_session),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet in csrc session\",\n                insert_multiple_extension_headers_into_packet_in_csrc_session),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet with payload in bundled session\",\n                insert_multiple_extension_headers_into_packet_with_payload_in_csrc_session),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet with detached payload in bundled session\",\n                insert_multiple_extension_headers_into_packet_with_detached_payload_in_csrc_session),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet in csrc bundled session\",\n                insert_multiple_extension_headers_into_packet_in_csrc_bundled_session),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet with payload in csrc bundled session\",\n                insert_multiple_extension_headers_into_packet_with_payload_in_csrc_bundled_session),\n    TEST_NO_TAG(\"Insert multiple extension headers into a packet with detached payload in csrc bundled session\",\n                insert_multiple_extension_headers_into_packet_with_detached_payload_in_csrc_bundled_session),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet\", insert_client_to_mixer_into_packet),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet with payload\",\n                insert_client_to_mixer_into_packet_with_payload),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet with detached payload\",\n                insert_client_to_mixer_into_packet_with_detached_payload),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet in bundled session\",\n                insert_client_to_mixer_into_packet_in_bundled_session),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet with payload in bundled session\",\n                insert_client_to_mixer_into_packet_with_payload_in_bundled_session),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet with detached payload in bundled session\",\n                insert_client_to_mixer_into_packet_with_detached_payload_in_bundled_session),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet in csrc session\",\n                insert_client_to_mixer_into_packet_in_csrc_session),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet with payload in csrc session\",\n                insert_client_to_mixer_into_packet_with_payload_in_csrc_session),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet with detached payload in csrc session\",\n                insert_client_to_mixer_into_packet_with_detached_payload_in_csrc_session),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet in csrc bundled session\",\n                insert_client_to_mixer_into_packet_in_csrc_bundled_session),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet with payload in csrc bundled session\",\n                insert_client_to_mixer_into_packet_with_payload_in_csrc_bundled_session),\n    TEST_NO_TAG(\"Insert client to mixer audio level into a packet with detached payload in csrc bundled session\",\n                insert_client_to_mixer_into_packet_with_detached_payload_in_csrc_bundled_session),\n    TEST_NO_TAG(\"Insert mixer to client audio level into a packet\", insert_mixer_to_client_into_packet),\n    TEST_NO_TAG(\"Insert mixer to client audio level into a packet with payload\",\n                insert_mixer_to_client_into_packet_with_payload),\n    TEST_NO_TAG(\"Insert mixer to client audio level into a packet in bundled session\",\n                insert_mixer_to_client_into_packet_in_bundled_session),\n    TEST_NO_TAG(\"Insert mixer to client audio level into a packet with payload in bundled session\",\n                insert_mixer_to_client_into_packet_with_payload_in_bundled_session),\n    TEST_NO_TAG(\"Insert mixer to client audio level into a packet using create packet with mixer\",\n                insert_mixer_to_client_into_packet_use_create_with_mixer),\n    TEST_NO_TAG(\"Insert mixer to client audio level into a packet with payload using create packet with mixer\",\n                insert_mixer_to_client_into_packet_with_payload_use_create_with_mixer),\n    TEST_NO_TAG(\"Insert mixer to client audio level into a packet in bundled session using create packet with mixer\",\n                insert_mixer_to_client_into_packet_in_bundled_session_use_create_with_mixer),\n    TEST_NO_TAG(\"Insert mixer to client audio level into a packet with payload in bundled session using create packet \"\n                \"with mixer\",\n                insert_mixer_to_client_into_packet_with_payload_in_bundled_session_use_create_with_mixer),\n    TEST_NO_TAG(\"Insert frame marking into a packet\", insert_frame_marking_into_packet),\n    TEST_NO_TAG(\"Insert frame marking into a packet with payload\", insert_frame_marking_into_packet_with_payload),\n    TEST_NO_TAG(\"Insert frame marking into a packet in bundled session\",\n                insert_frame_marking_into_packet_in_bundled_session),\n    TEST_NO_TAG(\"Insert frame marking into a packet with payload in bundled session\",\n                insert_frame_marking_into_packet_with_payload_in_bundled_session),\n    TEST_NO_TAG(\"Padding\", padding_test),\n    TEST_NO_TAG(\"Remap extension header ids from packet\", remap_extension_header_ids_from_packet),\n    TEST_NO_TAG(\"Adding existing extensions into packet\", add_existing_extensions_to_packet)};\n\ntest_suite_t extension_header_test_suite = {\n    \"Extension header\",               // Name of test suite\n    tester_before_all,                // Before all callback\n    tester_after_all,                 // After all callback\n    tester_before_each,               // Before each callback\n    NULL,                             // After each callback\n    sizeof(tests) / sizeof(tests[0]), // Size of test table\n    tests                             // Table of test suite\n};\n"
  },
  {
    "path": "tester/fec_tester.cc",
    "content": "\n#include \"fecstream/fec-stream-stats.h\"\n#include \"fecstream/fecstream.h\"\n#include \"ortp_tester.h\"\n#include <numeric>\n\nusing namespace ortp;\n\nstatic mblk_t *\nnewPacketWithLetter(struct _RtpSession *session, int seqnum, uint32_t timestamp, uint8_t car, size_t packet_size) {\n\tmblk_t *packet = NULL;\n\tpacket = rtp_session_create_packet_header(session, packet_size); // reserve packet_size after the header\n\tmemset(packet->b_wptr, car, packet_size);\n\tpacket->b_wptr += packet_size;\n\trtp_set_seqnumber(packet, seqnum);\n\trtp_set_timestamp(packet, timestamp);\n\treturn packet;\n}\n\nstatic bool_t compare_sizes(mblk_t *ma, mblk_t *mb) {\n\tsize_t sizea = msgdsize(ma);\n\tsize_t sizeb = msgdsize(mb);\n\treturn sizea == sizeb;\n}\n\nstatic bool_t compare_header_fields(mblk_t *ma, mblk_t *mb) {\n\n\tuint16_t cc = rtp_get_cc(ma);\n\tuint16_t ext = rtp_get_extbit(ma);\n\n\treturn (rtp_get_version(ma) == rtp_get_version(mb)) && (rtp_get_padbit(ma) == rtp_get_padbit(mb)) &&\n\t       (cc == rtp_get_cc(mb)) && (ext == rtp_get_extbit(mb)) && (rtp_get_markbit(ma) == rtp_get_markbit(mb)) &&\n\t       (rtp_get_payload_type(ma) == rtp_get_payload_type(mb)) && (rtp_get_seqnumber(ma) == rtp_get_seqnumber(mb)) &&\n\t       (rtp_get_ssrc(ma) == rtp_get_ssrc(mb));\n}\n\nstatic bool_t compare_csrc_fields(mblk_t *ma, mblk_t *mb) {\n\tuint16_t cc = rtp_get_cc(ma);\n\tif (cc != rtp_get_cc(mb)) return FALSE;\n\tfor (uint8_t i = 0; i < cc; i++) {\n\t\tif (rtp_get_csrc(ma, i) != rtp_get_csrc(mb, i)) return FALSE;\n\t}\n\treturn TRUE;\n}\n\nstatic bool_t compare_ext_headers(mblk_t *ma, mblk_t *mb) {\n\tuint8_t *data_ext_a = NULL;\n\tuint8_t *data_ext_b = NULL;\n\tuint16_t profile_ext_a, profile_ext_b;\n\tint size_ext_a, size_ext_b;\n\tuint16_t ext = rtp_get_extbit(ma);\n\n\tif (ext) {\n\n\t\tif (ext != rtp_get_extbit(mb)) return FALSE;\n\n\t\tsize_ext_a = rtp_get_extheader(ma, &profile_ext_a, &data_ext_a);\n\t\tsize_ext_b = rtp_get_extheader(mb, &profile_ext_b, &data_ext_b);\n\t\tif (size_ext_a != size_ext_b) return FALSE;\n\t\tif (!((profile_ext_a == profile_ext_b) && (memcmp(data_ext_a, data_ext_b, size_ext_a) == 0))) return FALSE;\n\t}\n\treturn TRUE;\n}\n\nstatic bool_t compare_payloads(mblk_t *ma, mblk_t *mb) {\n\tuint8_t *payload_a = NULL;\n\tuint8_t *payload_b = NULL;\n\tint size_payload_a, size_payload_b;\n\n\tsize_payload_a = rtp_get_payload(ma, &payload_a);\n\tsize_payload_b = rtp_get_payload(mb, &payload_b);\n\tif (size_payload_a != size_payload_b) return FALSE;\n\tif (memcmp(payload_a, payload_b, size_payload_a) != 0) return FALSE;\n\treturn TRUE;\n}\n\nstatic bool_t packets_are_equals(mblk_t *ma, mblk_t *mb) {\n\tif (!compare_sizes(ma, mb)) return FALSE;\n\tif (!compare_header_fields(ma, mb)) return FALSE;\n\tif (!compare_csrc_fields(ma, mb)) return FALSE;\n\tif (!compare_ext_headers(ma, mb)) return FALSE;\n\tif (!compare_payloads(ma, mb)) return FALSE;\n\treturn TRUE;\n}\n\nstatic void fec_params_update_test(void) {\n\tFecParamsController params(42);\n\tBC_ASSERT_EQUAL(params.getRepairWindow(), 42, uint32_t, \"%u\");\n\n\tBC_ASSERT_EQUAL(params.getL(), 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.getD(), 0, int, \"%d\");\n\tBC_ASSERT_FALSE(params.is2D());\n\tBC_ASSERT_FALSE(params.getEnabled());\n\n\tparams.updateParams(0);\n\tBC_ASSERT_EQUAL(params.getL(), 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.getD(), 0, int, \"%d\");\n\tBC_ASSERT_FALSE(params.is2D());\n\tBC_ASSERT_FALSE(params.getEnabled());\n\n\tparams.updateParams(1);\n\tBC_ASSERT_EQUAL(params.getL(), 10, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.getD(), 0, int, \"%d\");\n\tBC_ASSERT_FALSE(params.is2D());\n\tBC_ASSERT_TRUE(params.getEnabled());\n\n\tparams.updateParams(2);\n\tBC_ASSERT_EQUAL(params.getL(), 5, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.getD(), 5, int, \"%d\");\n\tBC_ASSERT_FALSE(params.is2D());\n\tBC_ASSERT_TRUE(params.getEnabled());\n\n\tparams.updateParams(3);\n\tBC_ASSERT_EQUAL(params.getL(), 5, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.getD(), 5, int, \"%d\");\n\tBC_ASSERT_TRUE(params.is2D());\n\tBC_ASSERT_TRUE(params.getEnabled());\n\n\tparams.updateParams(4);\n\tBC_ASSERT_EQUAL(params.getL(), 4, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.getD(), 4, int, \"%d\");\n\tBC_ASSERT_TRUE(params.is2D());\n\tBC_ASSERT_TRUE(params.getEnabled());\n\n\tparams.updateParams(5);\n\tBC_ASSERT_EQUAL(params.getL(), 3, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.getD(), 3, int, \"%d\");\n\tBC_ASSERT_TRUE(params.is2D());\n\tBC_ASSERT_TRUE(params.getEnabled());\n\n\t// this FEC level doesn't exist, the parameters are unchanged\n\tparams.updateParams(42);\n\tBC_ASSERT_EQUAL(params.getL(), 3, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.getD(), 3, int, \"%d\");\n\tBC_ASSERT_TRUE(params.is2D());\n\tBC_ASSERT_TRUE(params.getEnabled());\n}\n\nstatic void fec_params_level_test(void) {\n\tFecParamsController params(200000);\n\tfloat eps = 0.000001;\n\n\t// check the thresholds of loss rates the find the best fec level, for the cases with low, medium and high\n\t// bandwidth available, when the measurement of the current overhead is 0\n\tfloat crt_ov = 0.;\n\tfloat new_ov = 0;\n\tint low_bw = 10000;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(2.9, low_bw, crt_ov, &new_ov), 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(3.1, low_bw, crt_ov, &new_ov), 1, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(4.6, low_bw, crt_ov, &new_ov), 1, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(4.7, low_bw, crt_ov, &new_ov), 2, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(20.0, low_bw, crt_ov, &new_ov), 2, int, \"%d\");\n\tint med_bw = 200000;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.4, med_bw, crt_ov, &new_ov), 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.6, med_bw, crt_ov, &new_ov), 1, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(1.2, med_bw, crt_ov, &new_ov), 1, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(1.4, med_bw, crt_ov, &new_ov), 2, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(20.0, med_bw, crt_ov, &new_ov), 2, int, \"%d\");\n\tint high_bw = 400000;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0., high_bw, crt_ov, &new_ov), 1, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.17, high_bw, crt_ov, &new_ov), 1, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.18, high_bw, crt_ov, &new_ov), 2, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.51, high_bw, crt_ov, &new_ov), 2, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.53, high_bw, crt_ov, &new_ov), 3, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(20.0, high_bw, crt_ov, &new_ov), 3, int, \"%d\");\n\n\t// check that the best FEC level is 0 when the loss rate is too high\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(20.1, low_bw, crt_ov, &new_ov), 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(20.1, med_bw, crt_ov, &new_ov), 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(20.1, high_bw, crt_ov, &new_ov), 0, int, \"%d\");\n\n\t// check the value of the estimated overhead when the measurement of the current overhead is 0\n\tfloat overhead_factor = 2.;\n\tparams.estimateBestLevel(0., high_bw, crt_ov, &new_ov);\n\tBC_ASSERT_EQUAL(new_ov, overhead_factor * 0.1, float, \"%f\");\n\tparams.estimateBestLevel(0.2, high_bw, crt_ov, &new_ov);\n\tBC_ASSERT_EQUAL(new_ov, overhead_factor * 0.2, float, \"%f\");\n\tparams.estimateBestLevel(0.6, high_bw, crt_ov, &new_ov);\n\tBC_ASSERT_EQUAL(new_ov, overhead_factor * 0.4, float, \"%f\");\n\tparams.estimateBestLevel(0.75, high_bw, crt_ov, &new_ov);\n\tBC_ASSERT_EQUAL(new_ov, overhead_factor * 0.4, float, \"%f\");\n\tparams.estimateBestLevel(2.0, high_bw, crt_ov, &new_ov);\n\tBC_ASSERT_EQUAL(new_ov, overhead_factor * 0.4, float, \"%f\");\n\n\t// check the fec level and the estimated overhead when it is not necessary to reduce the fec level\n\tcrt_ov = 0.5;\n\tparams.updateParams(3);\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0., high_bw, crt_ov, &new_ov), 1, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, 0.1 / 0.4 * crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.4, high_bw, crt_ov, &new_ov), 2, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, 0.2 / 0.4 * crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.7, high_bw, crt_ov, &new_ov), 3, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, 0.4 / 0.4 * crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.8, high_bw, crt_ov, &new_ov), 4, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, 0.5 / 0.4 * crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(1.2, high_bw, crt_ov, &new_ov), 5, int, \"%d\");\n\tBC_ASSERT_TRUE((new_ov < 2. / 3. / 0.4 * crt_ov + eps) && (new_ov > 2. / 3. / 0.4 * crt_ov - eps));\n\n\t// check the range of loss rates when the FEC level doesn't  change, with theortical overhead value\n\tparams.updateParams(2);\n\tcrt_ov = 0.2;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(2.8, med_bw, crt_ov, &new_ov), 2, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.4, high_bw, crt_ov, &new_ov), 2, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tparams.updateParams(3);\n\tcrt_ov = 0.4;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(2.9, med_bw, crt_ov, &new_ov), 3, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(3.6, med_bw, crt_ov, &new_ov), 3, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.53, high_bw, crt_ov, &new_ov), 3, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.69, high_bw, crt_ov, &new_ov), 3, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tparams.updateParams(4);\n\tcrt_ov = 0.5;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(3.7, med_bw, crt_ov, &new_ov), 4, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(4.9, med_bw, crt_ov, &new_ov), 4, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.75, high_bw, crt_ov, &new_ov), 4, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(0.99, high_bw, crt_ov, &new_ov), 4, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tparams.updateParams(5);\n\tcrt_ov = 2. / 3.;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(5.0, med_bw, crt_ov, &new_ov), 5, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(20., med_bw, crt_ov, &new_ov), 5, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(1.0, high_bw, crt_ov, &new_ov), 5, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(20., high_bw, crt_ov, &new_ov), 5, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, crt_ov, float, \"%f\");\n\n\t// check the fec level and the estimated overhead when it is necessary to reduce the fec level to reduce the\n\t// overhead\n\tparams.updateParams(5);\n\tcrt_ov = 1.1;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(6., high_bw, crt_ov, &new_ov), 4, int, \"%d\");\n\tfloat expected_ov = 0.5 * 3. / 2. * crt_ov;\n\tBC_ASSERT_TRUE((new_ov < expected_ov + eps) && (new_ov > expected_ov - eps));\n\tcrt_ov = 1.4;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(6., high_bw, crt_ov, &new_ov), 3, int, \"%d\");\n\texpected_ov = 0.4 * 3. / 2. * crt_ov;\n\tBC_ASSERT_TRUE((new_ov < expected_ov + eps) && (new_ov > expected_ov - eps));\n\tcrt_ov = 2.9;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(6., high_bw, crt_ov, &new_ov), 2, int, \"%d\");\n\texpected_ov = 0.2 * 3. / 2. * crt_ov;\n\tBC_ASSERT_TRUE((new_ov < expected_ov + eps) && (new_ov > expected_ov - eps));\n\tcrt_ov = 5.9;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(6., high_bw, crt_ov, &new_ov), 1, int, \"%d\");\n\texpected_ov = 0.1 * 3. / 2. * crt_ov;\n\tBC_ASSERT_TRUE((new_ov < expected_ov + eps) && (new_ov > expected_ov - eps));\n\tcrt_ov = 5.9;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(6., high_bw, crt_ov, &new_ov), 1, int, \"%d\");\n\texpected_ov = 0.1 * 3. / 2. * crt_ov;\n\tBC_ASSERT_TRUE((new_ov < expected_ov + eps) && (new_ov > expected_ov - eps));\n\tcrt_ov = 6.1;\n\tBC_ASSERT_EQUAL(params.estimateBestLevel(6., high_bw, crt_ov, &new_ov), 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(new_ov, 0., float, \"%f\");\n\n\t// check that fec parameters haven't changed\n\tBC_ASSERT_EQUAL(params.getL(), 3, int, \"%d\");\n\tBC_ASSERT_EQUAL(params.getD(), 3, int, \"%d\");\n\tBC_ASSERT_TRUE(params.getEnabled());\n\tBC_ASSERT_TRUE(params.is2D());\n}\n\nstatic void bitstring_add_test(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\trtp_session_set_payload_type(session, 98);\n\tmblk_t *packetA = newPacketWithLetter(session, 0, 123456, 'a', 150);\n\tmblk_t *packetB = newPacketWithLetter(session, 1, 456789, 'b', 160);\n\n\tBitstring bsA(packetA);\n\tBitstring bsB(packetB);\n\tBC_ASSERT_EQUAL(bsA.getTimestamp(), 123456, uint32_t, \"%u\");\n\tBC_ASSERT_EQUAL(bsB.getTimestamp(), 456789, uint32_t, \"%u\");\n\tBC_ASSERT_EQUAL(bsA.getLength(), 150, uint32_t, \"%u\");\n\tBC_ASSERT_EQUAL(bsB.getLength(), 160, uint32_t, \"%u\");\n\n\tbsA.add(bsB);\n\tuint16_t header = bsA.getHeader();\n\tuint16_t expectedHeader = 0;\n\tint expectedTs = (123456 ^ 456789);\n\tint expectedLength = (150 ^ 160);\n\n\tBC_ASSERT_EQUAL(header, expectedHeader, uint16_t, \"%d\");\n\tBC_ASSERT_EQUAL(bsA.getLength(), expectedLength, int, \"%d\");\n\tBC_ASSERT_EQUAL(bsA.getTimestamp(), expectedTs, int, \"%d\");\n\n\tfreemsg(packetA);\n\tfreemsg(packetB);\n\trtp_session_destroy(session);\n}\n\nstatic void source_packet_get_payload_test(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\trtp_session_set_payload_type(session, 98);\n\tuint8_t *rptr = NULL;\n\tmblk_t *packetA = newPacketWithLetter(session, 0, 123456, 'a', 150);\n\tFecSourcePacket pA(packetA);\n\tsize_t sizeA = pA.getPayloadBuffer(&rptr);\n\n\tBC_ASSERT_EQUAL(sizeA, 150, size_t, \"%zu\");\n\n\trtp_session_destroy(session);\n}\n\nstatic void source_packet_add_payload_test1(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\trtp_session_set_payload_type(session, 98);\n\tuint8_t *rptr = NULL;\n\tmblk_t *packetA = newPacketWithLetter(session, 0, 123456, 'a', 150);\n\tmblk_t *packetB = newPacketWithLetter(session, 0, 456789, 'a', 150);\n\n\tFecSourcePacket pA(packetA);\n\tFecSourcePacket pB(packetB);\n\n\tpA.addPayload(pB);\n\tuint8_t expectedPayload[150] = {0};\n\n\tsize_t sizeA = pA.getPayloadBuffer(&rptr);\n\tBC_ASSERT_EQUAL(sizeA, 150, size_t, \"%zu\");\n\tBC_ASSERT_TRUE(memcmp(rptr, expectedPayload, sizeA) == 0);\n\n\trtp_session_destroy(session);\n}\n\nstatic void source_packet_add_payload_test2(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\trtp_session_set_payload_type(session, 98);\n\tuint8_t *rptr = NULL;\n\tmblk_t *packetA = newPacketWithLetter(session, 0, 123456, 'a', 150);\n\tmblk_t *packetB = newPacketWithLetter(session, 0, 456789, 'b', 180);\n\n\tFecSourcePacket pA(packetA);\n\tFecSourcePacket pB(packetB);\n\n\tpA.addPayload(pB);\n\tuint8_t expectedPayload[150] = {0};\n\tfor (int i = 0; i < 150; i++) {\n\t\texpectedPayload[i] = 'a' ^ 'b';\n\t}\n\n\tsize_t sizeA = pA.getPayloadBuffer(&rptr);\n\tBC_ASSERT_EQUAL(sizeA, 150, size_t, \"%zu\");\n\tBC_ASSERT_TRUE(memcmp(rptr, expectedPayload, sizeA) == 0);\n\n\trtp_session_destroy(session);\n}\n\nstatic void source_packet_add_payload_test3(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\trtp_session_set_payload_type(session, 98);\n\tuint8_t *rptr = NULL;\n\tmblk_t *packetA = newPacketWithLetter(session, 0, 123456, 'a', 150);\n\tmblk_t *packetB = newPacketWithLetter(session, 0, 456789, 'b', 120);\n\n\tFecSourcePacket pA(packetA);\n\tFecSourcePacket pB(packetB);\n\n\tpA.addPayload(pB);\n\tuint8_t expectedPayload[180] = {0};\n\tfor (int i = 0; i < 180; i++) {\n\t\tif (i < 120) expectedPayload[i] = 'a' ^ 'b';\n\t\telse expectedPayload[i] = 'a';\n\t}\n\n\tsize_t sizeA = pA.getPayloadBuffer(&rptr);\n\tBC_ASSERT_EQUAL(sizeA, 150, size_t, \"%zu\");\n\tBC_ASSERT_TRUE(memcmp(rptr, expectedPayload, sizeA) == 0);\n\n\trtp_session_destroy(session);\n}\n\nstatic void repair_packet_bitstring_test(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tmblk_t *packetA = newPacketWithLetter(session, 0, 123456, 'a', 150);\n\n\tFecSourcePacket sourceA(packetA);\n\tFecRepairPacket repair(session, session, 0, 5, 0);\n\n\tauto bsA = sourceA.getBitstring();\n\trepair.addBitstring(bsA);\n\tauto extracted = repair.extractBitstring();\n\n\tBC_ASSERT_EQUAL(extracted.getHeader(), bsA.getHeader(), uint16_t, \"%d\");\n\tBC_ASSERT_EQUAL(extracted.getLength(), bsA.getLength(), uint16_t, \"%d\");\n\tBC_ASSERT_EQUAL(extracted.getTimestamp(), bsA.getTimestamp(), uint32_t, \"%d\");\n\n\trtp_session_destroy(session);\n}\n\nstatic void repair_packet_add_payload1(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\trtp_session_set_payload_type(session, 98);\n\n\tmblk_t *packetA = newPacketWithLetter(session, 0, 123456, 'a', 150);\n\tFecSourcePacket sourceA(packetA);\n\tFecRepairPacket repair(session, session, 0, 5, 1);\n\n\trepair.addPayload(sourceA);\n\tuint8_t *expectedBuffer = NULL;\n\tuint8_t *buffer = NULL;\n\tsize_t expectedSize = sourceA.getPayloadBuffer(&expectedBuffer);\n\tsize_t size = repair.repairPayloadStart(&buffer);\n\n\tBC_ASSERT_EQUAL(size, expectedSize, size_t, \"%zu\");\n\tBC_ASSERT_TRUE(memcmp(buffer, expectedBuffer, size) == 0);\n\n\trtp_session_destroy(session);\n}\n\nstatic void repair_packet_seqnumListNonInterleaved_test(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\t{\n\t\tuint8_t L = 3;\n\t\tuint8_t D = 0;\n\t\tFecRepairPacket repair(session, session, 0, L, D);\n\t\tauto liste = repair.createSequenceNumberList();\n\t\tBC_ASSERT_EQUAL(liste.size(), 3, size_t, \"%zu\");\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tBC_ASSERT_EQUAL(liste[i], i, uint16_t, \"%u\");\n\t\t}\n\t\tBC_ASSERT_EQUAL(repair.getL(), L, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(repair.getD(), D, int, \"%d\");\n\t}\n\t{\n\t\tuint8_t L = 3;\n\t\tuint8_t D = 1;\n\t\tFecRepairPacket repair(session, session, 0, L, D);\n\t\tauto liste = repair.createSequenceNumberList();\n\t\tBC_ASSERT_EQUAL(liste.size(), 3, size_t, \"%zu\");\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tBC_ASSERT_EQUAL(liste[i], i, uint16_t, \"%u\");\n\t\t}\n\t\tBC_ASSERT_EQUAL(repair.getL(), L, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(repair.getD(), D, int, \"%d\");\n\t}\n\n\trtp_session_destroy(session);\n}\n\nstatic void repair_packet_seqnumListInterleaved_test(void) {\n\tuint8_t L = 3;\n\tuint8_t D = 3;\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tFecRepairPacket repair(session, session, 0, L, D);\n\tauto liste = repair.createSequenceNumberList();\n\tBC_ASSERT_EQUAL(liste.size(), 3, size_t, \"%zu\");\n\tint seqnum = 0;\n\tfor (int i = 0; i < 3; i++) {\n\t\tBC_ASSERT_EQUAL(liste[i], seqnum, uint16_t, \"%u\");\n\t\tseqnum += 3;\n\t}\n\tBC_ASSERT_EQUAL(repair.getL(), L, int, \"%d\");\n\tBC_ASSERT_EQUAL(repair.getD(), D, int, \"%d\");\n\n\trtp_session_destroy(session);\n}\n\nstatic void encoder_init_1D_non_interleaved_test(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\n\tFecParamsController params(200000);\n\tparams.updateParams(1);\n\tFecEncoder encoder(&params);\n\tBC_ASSERT_TRUE(encoder.isEmpty());\n\tBC_ASSERT_FALSE(encoder.isFull());\n\n\tencoder.init(session, session);\n\n\tauto rowRepair0 = encoder.getRowRepair(0);\n\tBC_ASSERT_PTR_NOT_NULL(rowRepair0);\n\tif (rowRepair0) {\n\t\tBC_ASSERT_EQUAL(rowRepair0->getL(), 10, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(rowRepair0->getD(), 0, int, \"%d\");\n\t}\n\tauto rowRepair1 = encoder.getRowRepair(1);\n\tBC_ASSERT_PTR_NULL(rowRepair1);\n\tauto colRepair = encoder.getColRepair(0);\n\tBC_ASSERT_PTR_NULL(colRepair);\n\n\trtp_session_destroy(session);\n}\n\nstatic void encoder_init_1D_interleaved_test(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\n\tFecParamsController params(200000);\n\t// params.updateParams(1);\n\tparams.updateParams(2);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\tBC_ASSERT_TRUE(encoder.isEmpty());\n\tBC_ASSERT_FALSE(encoder.isFull());\n\n\tauto rowRepair0 = encoder.getRowRepair(0);\n\tBC_ASSERT_PTR_NULL(rowRepair0);\n\tfor (int i = 0; i < 5; i++) {\n\t\tauto colRepair = encoder.getColRepair(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(colRepair);\n\t\tif (colRepair) {\n\t\t\tBC_ASSERT_EQUAL(colRepair->getL(), 5, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(colRepair->getD(), 5, int, \"%d\");\n\t\t}\n\t}\n\tauto colRepair5 = encoder.getColRepair(5);\n\tBC_ASSERT_PTR_NULL(colRepair5);\n\trtp_session_destroy(session);\n}\n\nstatic void encoder_init_2D_test(void) {\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\n\tFecParamsController params(200000);\n\tparams.updateParams(3);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\n\tfor (int i = 0; i < 5; i++) {\n\t\tauto rowRepair = encoder.getRowRepair(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(rowRepair);\n\t\tif (rowRepair) {\n\t\t\tBC_ASSERT_EQUAL(rowRepair->getL(), 5, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(rowRepair->getD(), 1, int, \"%d\");\n\t\t}\n\t\tauto colRepair = encoder.getColRepair(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(colRepair);\n\t\tif (colRepair) {\n\t\t\tBC_ASSERT_EQUAL(colRepair->getL(), 5, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(colRepair->getD(), 5, int, \"%d\");\n\t\t}\n\t}\n\tauto rowRepair5 = encoder.getRowRepair(5);\n\tBC_ASSERT_PTR_NULL(rowRepair5);\n\tauto colRepair5 = encoder.getColRepair(5);\n\tBC_ASSERT_PTR_NULL(colRepair5);\n\trtp_session_destroy(session);\n}\n\nstatic void encoder_update_test(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tFecParamsController params(200000);\n\tparams.updateParams(2);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\tencoder.update(5, 0, false);\n\t{\n\t\tBC_ASSERT_TRUE(encoder.isEmpty());\n\t\tBC_ASSERT_FALSE(encoder.isFull());\n\t\tauto rowRepair0 = encoder.getRowRepair(0);\n\t\tBC_ASSERT_PTR_NOT_NULL(rowRepair0);\n\t\tif (rowRepair0) {\n\t\t\tBC_ASSERT_EQUAL(rowRepair0->getL(), 5, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(rowRepair0->getD(), 0, int, \"%d\");\n\t\t}\n\t\tauto rowRepair1 = encoder.getRowRepair(1);\n\t\tBC_ASSERT_PTR_NULL(rowRepair1);\n\t\tauto colRepair = encoder.getColRepair(0);\n\t\tBC_ASSERT_PTR_NULL(colRepair);\n\t}\n\tencoder.update(5, 10, false);\n\t{\n\t\tBC_ASSERT_TRUE(encoder.isEmpty());\n\t\tBC_ASSERT_FALSE(encoder.isFull());\n\n\t\tauto rowRepair0 = encoder.getRowRepair(0);\n\t\tBC_ASSERT_PTR_NULL(rowRepair0);\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tauto colRepair = encoder.getColRepair(i);\n\t\t\tBC_ASSERT_PTR_NOT_NULL(colRepair);\n\t\t\tif (colRepair) {\n\t\t\t\tBC_ASSERT_EQUAL(colRepair->getL(), 5, int, \"%d\");\n\t\t\t\tBC_ASSERT_EQUAL(colRepair->getD(), 10, int, \"%d\");\n\t\t\t}\n\t\t}\n\t\tauto colRepair5 = encoder.getColRepair(5);\n\t\tBC_ASSERT_PTR_NULL(colRepair5);\n\t}\n\tencoder.update(3, 4, true);\n\t{\n\t\tBC_ASSERT_TRUE(encoder.isEmpty());\n\t\tBC_ASSERT_FALSE(encoder.isFull());\n\t\tfor (int i = 0; i < 4; i++) {\n\t\t\tauto rowRepair = encoder.getRowRepair(i);\n\t\t\tBC_ASSERT_PTR_NOT_NULL(rowRepair);\n\t\t\tif (rowRepair) {\n\t\t\t\tBC_ASSERT_EQUAL(rowRepair->getL(), 3, int, \"%d\");\n\t\t\t\tBC_ASSERT_EQUAL(rowRepair->getD(), 1, int, \"%d\");\n\t\t\t}\n\t\t}\n\t\tauto rowRepair4 = encoder.getRowRepair(4);\n\t\tBC_ASSERT_PTR_NULL(rowRepair4);\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tauto colRepair = encoder.getColRepair(i);\n\t\t\tBC_ASSERT_PTR_NOT_NULL(colRepair);\n\t\t\tif (colRepair) {\n\t\t\t\tBC_ASSERT_EQUAL(colRepair->getL(), 3, int, \"%d\");\n\t\t\t\tBC_ASSERT_EQUAL(colRepair->getD(), 4, int, \"%d\");\n\t\t\t}\n\t\t}\n\t\tauto colRepair3 = encoder.getColRepair(3);\n\t\tBC_ASSERT_PTR_NULL(colRepair3);\n\t}\n\trtp_session_destroy(session);\n}\n\nstatic void encoder_add_1D_test(void) {\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tFecParamsController params(200000);\n\tparams.updateParams(1);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\tuint8_t *sourcePayload = NULL;\n\tuint8_t *repairPayload = NULL;\n\tmblk_t *packet = newPacketWithLetter(session, 0, 123456, 'a', 150);\n\tFecSourcePacket source(packet);\n\tencoder.add(source);\n\tauto repair = encoder.getRowRepair(0);\n\tBC_ASSERT_PTR_NOT_NULL(repair);\n\tif (repair) {\n\t\tsize_t repairSize = repair->repairPayloadStart(&repairPayload);\n\t\tsize_t sourceSize = source.getPayloadBuffer(&sourcePayload);\n\t\tBC_ASSERT_TRUE(repair->extractBitstring().equals(source.getBitstring()));\n\t\tBC_ASSERT_EQUAL(repairSize, sourceSize, size_t, \"%zu\");\n\t\tBC_ASSERT_TRUE(memcmp(sourcePayload, repairPayload, sourceSize) == 0);\n\t}\n\tauto repairCol = encoder.getColRepair(0);\n\tBC_ASSERT_PTR_NULL(repairCol);\n\trtp_session_destroy(session);\n}\n\nstatic void encoder_add_1D_interleaved_test(void) {\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tFecParamsController params(200000);\n\tparams.updateParams(2);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\tencoder.update(5, 10, false);\n\tuint8_t *sourcePayload = NULL;\n\tuint8_t *repairPayload = NULL;\n\tstd::vector<std::shared_ptr<FecSourcePacket>> sources;\n\tfor (int i = 0; i < 5; i++) {\n\t\tmblk_t *packet = newPacketWithLetter(session, i, 123456, 'a', 150 + i * 10);\n\t\tauto source = std::make_shared<FecSourcePacket>(packet);\n\t\tsources.push_back(source);\n\t\tencoder.add(*source);\n\t}\n\tfor (int i = 0; i < 5; i++) {\n\t\tauto repair = encoder.getColRepair(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(repair);\n\t\tif (repair) {\n\t\t\tsize_t repairSize = repair->repairPayloadStart(&repairPayload);\n\t\t\tsize_t sourceSize = sources.at(i)->getPayloadBuffer(&sourcePayload);\n\t\t\tBC_ASSERT_TRUE(repair->extractBitstring().equals(sources.at(i)->getBitstring()));\n\t\t\tBC_ASSERT_EQUAL(repairSize, sourceSize, size_t, \"%zu\");\n\t\t\tBC_ASSERT_TRUE(memcmp(sourcePayload, repairPayload, sourceSize) == 0);\n\t\t}\n\t}\n\tauto repairRow = encoder.getRowRepair(0);\n\tBC_ASSERT_PTR_NULL(repairRow);\n\trtp_session_destroy(session);\n}\n\nstatic void encoder_add_2D_test(void) {\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tFecParamsController params(200000);\n\tparams.updateParams(3);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\tuint8_t *sourcePayload = NULL;\n\tuint8_t *repairPayload = NULL;\n\tstd::vector<std::shared_ptr<FecSourcePacket>> sources;\n\t{\n\t\tmblk_t *packet = newPacketWithLetter(session, 0, 123456, 'a', 150);\n\t\tauto source = std::make_shared<FecSourcePacket>(packet);\n\t\tsources.push_back(source);\n\t\tencoder.add(*source);\n\t}\n\t{\n\t\tauto repair = encoder.getRowRepair(0);\n\t\tBC_ASSERT_PTR_NOT_NULL(repair);\n\t\tif (repair) {\n\t\t\tsize_t repairSize = repair->repairPayloadStart(&repairPayload);\n\t\t\tsize_t sourceSize = sources.at(0)->getPayloadBuffer(&sourcePayload);\n\t\t\tBC_ASSERT_TRUE(repair->extractBitstring().equals(sources.at(0)->getBitstring()));\n\t\t\tBC_ASSERT_EQUAL(repairSize, sourceSize, size_t, \"%zu\");\n\t\t\tBC_ASSERT_TRUE(memcmp(sourcePayload, repairPayload, sourceSize) == 0);\n\t\t}\n\t}\n\tfor (int i = 1; i < 3; i++) {\n\t\tmblk_t *packet = newPacketWithLetter(session, i, 123456, 'a', 150 + i * 10);\n\t\tauto source = std::make_shared<FecSourcePacket>(packet);\n\t\tsources.push_back(source);\n\t\tencoder.add(*source);\n\t}\n\tfor (int i = 0; i < 3; i++) {\n\t\tauto repair = encoder.getColRepair(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(repair);\n\t\tif (repair) {\n\t\t\tsize_t repairSize = repair->repairPayloadStart(&repairPayload);\n\t\t\tsize_t sourceSize = sources.at(i)->getPayloadBuffer(&sourcePayload);\n\t\t\tBC_ASSERT_TRUE(repair->extractBitstring().equals(sources.at(i)->getBitstring()));\n\t\t\tBC_ASSERT_EQUAL(repairSize, sourceSize, size_t, \"%zu\");\n\t\t\tBC_ASSERT_TRUE(memcmp(sourcePayload, repairPayload, sourceSize) == 0);\n\t\t}\n\t}\n\trtp_session_destroy(session);\n}\n\nstatic void encoder_full_test(void) {\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tFecParamsController params(200000);\n\tparams.updateParams(1);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\tmblk_t *packet = NULL;\n\t// 1D non interleaved\n\tfor (int i = 0; i < 10; i++) {\n\t\tpacket = newPacketWithLetter(session, i, i * 60, 'a' + i, 150 + i);\n\t\tFecSourcePacket source(packet);\n\t\tencoder.add(source);\n\t\tint crtCol = i;\n\t\tint crtRow = 0;\n\t\tBC_ASSERT_EQUAL(encoder.getCurrentRow(), crtRow, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(encoder.getCurrentColumn(), crtCol, int, \"%d\");\n\t\tBC_ASSERT_FALSE(encoder.isColFull());\n\t\tif (i < 9) {\n\t\t\tBC_ASSERT_FALSE(encoder.isRowFull());\n\t\t\tBC_ASSERT_FALSE(encoder.isFull());\n\t\t} else {\n\t\t\tBC_ASSERT_TRUE(encoder.isRowFull());\n\t\t\tBC_ASSERT_TRUE(encoder.isFull());\n\t\t}\n\t}\n\tBC_ASSERT_TRUE(encoder.isFull());\n\tencoder.clear();\n\t// 1D interleaved\n\tencoder.update(4, 3, false);\n\tfor (int i = 0; i < 12; i++) {\n\t\tpacket = newPacketWithLetter(session, i, i * 60, 'a' + i, 150 + i);\n\t\tFecSourcePacket source(packet);\n\t\tencoder.add(source);\n\t\tint crtCol = i % 4;\n\t\tint crtRow = (i - crtCol) / 4;\n\t\tBC_ASSERT_EQUAL(encoder.getCurrentRow(), crtRow, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(encoder.getCurrentColumn(), crtCol, int, \"%d\");\n\t\tBC_ASSERT_FALSE(encoder.isRowFull());\n\t\tif (crtRow == 2) {\n\t\t\tBC_ASSERT_TRUE(encoder.isColFull());\n\t\t} else {\n\t\t\tBC_ASSERT_FALSE(encoder.isColFull());\n\t\t}\n\t\tif (i < 11) {\n\t\t\tBC_ASSERT_FALSE(encoder.isFull());\n\t\t} else {\n\t\t\tBC_ASSERT_TRUE(encoder.isFull());\n\t\t}\n\t}\n\tBC_ASSERT_TRUE(encoder.isFull());\n\tencoder.clear();\n\t// 2D\n\tencoder.update(4, 3, true);\n\tfor (int i = 0; i < 12; i++) {\n\t\tpacket = newPacketWithLetter(session, i, i * 60, 'a' + i, 150 + i);\n\t\tFecSourcePacket source(packet);\n\t\tencoder.add(source);\n\t\tint crtCol = i % 4;\n\t\tint crtRow = (i - crtCol) / 4;\n\t\tBC_ASSERT_EQUAL(encoder.getCurrentRow(), crtRow, int, \"%d\");\n\t\tBC_ASSERT_EQUAL(encoder.getCurrentColumn(), crtCol, int, \"%d\");\n\t\tif (crtCol == 3) {\n\t\t\tBC_ASSERT_TRUE(encoder.isRowFull());\n\t\t} else {\n\t\t\tBC_ASSERT_FALSE(encoder.isRowFull());\n\t\t}\n\t\tif (crtRow == 2) {\n\t\t\tBC_ASSERT_TRUE(encoder.isColFull());\n\t\t} else {\n\t\t\tBC_ASSERT_FALSE(encoder.isColFull());\n\t\t}\n\t\tif (i < 11) {\n\t\t\tBC_ASSERT_FALSE(encoder.isFull());\n\t\t} else {\n\t\t\tBC_ASSERT_TRUE(encoder.isFull());\n\t\t}\n\t}\n\tBC_ASSERT_TRUE(encoder.isFull());\n\trtp_session_destroy(session);\n}\n\nstatic void encoder_fill_test(void) {\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tFecParamsController params(200000);\n\tparams.updateParams(5);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\tfor (int i = 0; i < 9; i++) {\n\t\tmblk_t *packet = newPacketWithLetter(session, i, i * 20, 'a' + i, 10);\n\t\tFecSourcePacket source(packet);\n\t\tencoder.add(source);\n\t}\n\tuint8_t *rptr = nullptr;\n\tencoder.getRowRepair(0)->repairPayloadStart(&rptr);\n\tuint8_t value = ('a' ^ 'b' ^ 'c');\n\tBC_ASSERT_EQUAL(*rptr, value, uint8_t, \"%u\");\n\trptr = nullptr;\n\tencoder.getRowRepair(1)->repairPayloadStart(&rptr);\n\tvalue = ('d' ^ 'e' ^ 'f');\n\tBC_ASSERT_EQUAL(*rptr, value, uint8_t, \"%u\");\n\trptr = nullptr;\n\tencoder.getRowRepair(2)->repairPayloadStart(&rptr);\n\tvalue = ('g' ^ 'h' ^ 'i');\n\tBC_ASSERT_EQUAL(*rptr, value, uint8_t, \"%u\");\n\trptr = nullptr;\n\tencoder.getColRepair(0)->repairPayloadStart(&rptr);\n\tvalue = ('a' ^ 'd' ^ 'g');\n\tBC_ASSERT_EQUAL(*rptr, value, uint8_t, \"%u\");\n\trptr = nullptr;\n\tencoder.getColRepair(1)->repairPayloadStart(&rptr);\n\tvalue = ('b' ^ 'e' ^ 'h');\n\tBC_ASSERT_EQUAL(*rptr, value, uint8_t, \"%u\");\n\trptr = nullptr;\n\tencoder.getColRepair(2)->repairPayloadStart(&rptr);\n\tvalue = ('c' ^ 'f' ^ 'i');\n\tBC_ASSERT_EQUAL(*rptr, value, uint8_t, \"%u\");\n\trtp_session_destroy(session);\n}\n\nstatic void encoder_reset(void) {\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tFecParamsController params(200000);\n\tparams.updateParams(5);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\tmblk_t *expected = encoder.getRowRepairMblk(0);\n\tmblk_t *packet = NULL;\n\tencoder.reset(42);\n\tfor (int i = 0; i < 9; i++) {\n\t\tpacket = newPacketWithLetter(session, 42 + i, i * 60, 'a' + i, 150 + i);\n\t\tFecSourcePacket source(packet);\n\t\tencoder.add(source);\n\t}\n\tBC_ASSERT_FALSE(encoder.isEmpty());\n\tBC_ASSERT_TRUE(encoder.isFull());\n\tfor (int i = 0; i < 3; i++) {\n\t\tauto rowRepair = encoder.getRowRepair(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(rowRepair);\n\t\tif (rowRepair) {\n\t\t\tBC_ASSERT_EQUAL(rowRepair->getL(), 3, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(rowRepair->getD(), 1, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(rowRepair->getSeqnumBase(), 42 + i * 3, uint16_t, \"%u\");\n\t\t}\n\t\tauto colRepair = encoder.getColRepair(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(colRepair);\n\t\tif (colRepair) {\n\t\t\tBC_ASSERT_EQUAL(colRepair->getL(), 3, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(colRepair->getD(), 3, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(colRepair->getSeqnumBase(), 42 + i, uint16_t, \"%u\");\n\t\t}\n\t}\n\tencoder.reset(0);\n\tauto actual = encoder.getRowRepairMblk(0);\n\tBC_ASSERT_TRUE(packets_are_equals(expected, actual));\n\tBC_ASSERT_TRUE(encoder.isEmpty());\n\tBC_ASSERT_FALSE(encoder.isFull());\n\tfor (int i = 0; i < 3; i++) {\n\t\tauto rowRepair = encoder.getRowRepair(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(rowRepair);\n\t\tif (rowRepair) {\n\t\t\tBC_ASSERT_EQUAL(rowRepair->getL(), 3, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(rowRepair->getD(), 1, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(rowRepair->getSeqnumBase(), i * 3, uint16_t, \"%u\");\n\t\t}\n\t\tauto colRepair = encoder.getColRepair(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(colRepair);\n\t\tif (colRepair) {\n\t\t\tBC_ASSERT_EQUAL(colRepair->getL(), 3, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(colRepair->getD(), 3, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(colRepair->getSeqnumBase(), i, uint16_t, \"%u\");\n\t\t}\n\t}\n\trtp_session_destroy(session);\n\tfreemsg(expected);\n\tfreemsg(actual);\n}\n\nstatic void encoder_clear(void) {\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tFecParamsController params(200000);\n\tparams.updateParams(5);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\tmblk_t *packet = NULL;\n\tencoder.reset(42);\n\tfor (int i = 0; i < 4; i++) {\n\t\tpacket = newPacketWithLetter(session, 42 + i, i * 60, 'a' + i, 150 + i);\n\t\tFecSourcePacket source(packet);\n\t\tencoder.add(source);\n\t}\n\tBC_ASSERT_FALSE(encoder.isEmpty());\n\tBC_ASSERT_FALSE(encoder.isFull());\n\tfor (int i = 0; i < 3; i++) {\n\t\tauto rowRepair = encoder.getRowRepair(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(rowRepair);\n\t\tif (rowRepair) {\n\t\t\tBC_ASSERT_EQUAL(rowRepair->getL(), 3, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(rowRepair->getD(), 1, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(rowRepair->getSeqnumBase(), 42 + i * 3, uint16_t, \"%u\");\n\t\t}\n\t\tauto colRepair = encoder.getColRepair(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(colRepair);\n\t\tif (colRepair) {\n\t\t\tBC_ASSERT_EQUAL(colRepair->getL(), 3, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(colRepair->getD(), 3, int, \"%d\");\n\t\t\tBC_ASSERT_EQUAL(colRepair->getSeqnumBase(), 42 + i, uint16_t, \"%u\");\n\t\t}\n\t}\n\tencoder.clear();\n\tBC_ASSERT_TRUE(encoder.isEmpty());\n\tBC_ASSERT_FALSE(encoder.isFull());\n\tauto rowRepair = encoder.getRowRepair(0);\n\tBC_ASSERT_PTR_NULL(rowRepair);\n\tauto colRepair = encoder.getColRepair(0);\n\tBC_ASSERT_PTR_NULL(colRepair);\n\trtp_session_destroy(session);\n}\n\nstatic void overhead_estimation_test(void) {\n\tOverhead overhead;\n\tBC_ASSERT_EQUAL(overhead.computeOverheadEstimator(), 0., float, \"%f\");\n\tsize_t source_packets_size = 200.;\n\tsize_t repair_packets_size = source_packets_size + 64;\n\tfloat repair_size_sum = 0.;\n\tfloat source_size_sum = 0.;\n\tstd::vector<float> overheads;\n\tfloat overhead_measured = 0.;\n\tfloat check_overhead = 0.;\n\tint block_nb = 50;\n\tfloat eps = 0.000001;\n\n\t// check for fec param (3, 3)\n\toverhead.reset(1);\n\tBC_ASSERT_EQUAL(overhead.computeOverheadEstimator(), 0., float, \"%f\");\n\tfor (int i = 0; i < block_nb + 2; i++) {\n\t\tfor (int j = 1; j <= 9; j++) {\n\t\t\toverhead.sendSourcePacket(source_packets_size + j + i, 0);\n\t\t}\n\t\toverhead.sendRepairPacket(repair_packets_size + 3 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 6 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 7 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 8 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 9 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 9 + i, 0);\n\t\toverhead.encoderFull();\n\t\toverhead_measured = overhead.computeOverheadEstimator();\n\t\trepair_size_sum = static_cast<float>(6 * (repair_packets_size + i) + 42);\n\t\tsource_size_sum = static_cast<float>(9 * (source_packets_size + i) + 45);\n\t\toverheads.push_back(repair_size_sum / source_size_sum);\n\t\twhile (static_cast<int>(overheads.size()) > block_nb) {\n\t\t\toverheads.erase(overheads.begin());\n\t\t}\n\t\tif (overheads.size() < 5) {\n\t\t\tcheck_overhead = 0.;\n\t\t} else {\n\t\t\tcheck_overhead =\n\t\t\t    std::accumulate(overheads.begin(), overheads.end(), 0.) / static_cast<float>(overheads.size());\n\t\t}\n\t\tBC_ASSERT_LOWER(overhead_measured, check_overhead + eps, float, \"%f\");\n\t\tBC_ASSERT_GREATER(overhead_measured, check_overhead - eps, float, \"%f\");\n\t}\n\n\toverhead.sendSourcePacket(source_packets_size + 42, 0);\n\toverhead.sendRepairPacket(repair_packets_size + 42, 0);\n\toverhead.resetEncoder();\n\toverhead_measured = overhead.computeOverheadEstimator();\n\tBC_ASSERT_LOWER(overhead_measured, check_overhead + eps, float, \"%f\");\n\tBC_ASSERT_GREATER(overhead_measured, check_overhead - eps, float, \"%f\");\n\n\t// check for fec param (5, 0), 1D non interleaved\n\toverhead.reset(1);\n\toverhead_measured = overhead.computeOverheadEstimator();\n\tBC_ASSERT_EQUAL(overhead_measured, 0., float, \"%f\");\n\toverheads.clear();\n\tfor (int i = 0; i < block_nb + 2; i++) {\n\t\tfor (int j = 1; j <= 5; j++) {\n\t\t\toverhead.sendSourcePacket(source_packets_size + j + i, 0);\n\t\t}\n\t\toverhead.sendRepairPacket(repair_packets_size + 5 + i, 0);\n\t\toverhead.encoderFull();\n\t\toverhead_measured = overhead.computeOverheadEstimator();\n\t\trepair_size_sum = static_cast<float>(repair_packets_size + i + 5);\n\t\tsource_size_sum = static_cast<float>(5 * (source_packets_size + i) + 15);\n\t\toverheads.push_back(repair_size_sum / source_size_sum);\n\t\twhile (static_cast<int>(overheads.size()) > block_nb) {\n\t\t\toverheads.erase(overheads.begin());\n\t\t}\n\t\tif (overheads.size() < 5) {\n\t\t\tcheck_overhead = 0.;\n\t\t} else {\n\t\t\tcheck_overhead =\n\t\t\t    std::accumulate(overheads.begin(), overheads.end(), 0.) / static_cast<float>(overheads.size());\n\t\t}\n\t\tBC_ASSERT_LOWER(overhead_measured, check_overhead + eps, float, \"%f\");\n\t\tBC_ASSERT_GREATER(overhead_measured, check_overhead - eps, float, \"%f\");\n\t}\n\n\t// check for fec param (0, 3), 1D interleaved\n\toverhead.reset(3);\n\toverhead_measured = overhead.computeOverheadEstimator();\n\tBC_ASSERT_EQUAL(overhead_measured, 0., float, \"%f\");\n\toverheads.clear();\n\tfor (int i = 0; i < block_nb + 1; i++) {\n\t\tfor (int j = 0; j < 3; j++) {\n\t\t\tfor (int k = 0; k < 3; k++) {\n\t\t\t\toverhead.sendSourcePacket(source_packets_size + 1 + j * 3 + k + i, k);\n\t\t\t}\n\t\t}\n\t\toverhead.sendRepairPacket(repair_packets_size + 7 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 8 + i, 1);\n\t\toverhead.sendRepairPacket(repair_packets_size + 9 + i, 2);\n\t\toverhead.encoderFull();\n\t\toverhead_measured = overhead.computeOverheadEstimator();\n\t\tfloat repair_size_sum_0 = static_cast<float>(repair_packets_size + i + 7);\n\t\tfloat source_size_sum_0 = static_cast<float>(3 * (source_packets_size + i) + 12);\n\t\toverheads.push_back(repair_size_sum_0 / source_size_sum_0);\n\t\tfloat repair_size_sum_1 = static_cast<float>(repair_packets_size + i + 8);\n\t\tfloat source_size_sum_1 = static_cast<float>(3 * (source_packets_size + i) + 15);\n\t\toverheads.push_back(repair_size_sum_1 / source_size_sum_1);\n\t\tfloat repair_size_sum_2 = static_cast<float>(repair_packets_size + i + 9);\n\t\tfloat source_size_sum_2 = static_cast<float>(3 * (source_packets_size + i) + 18);\n\t\toverheads.push_back(repair_size_sum_2 / source_size_sum_2);\n\t\twhile (static_cast<int>(overheads.size()) > block_nb) {\n\t\t\toverheads.erase(overheads.begin());\n\t\t}\n\t\tif (overheads.size() < 5) {\n\t\t\tcheck_overhead = 0.;\n\t\t} else {\n\t\t\tcheck_overhead =\n\t\t\t    std::accumulate(overheads.begin(), overheads.end(), 0.) / static_cast<float>(overheads.size());\n\t\t}\n\t\tBC_ASSERT_LOWER(overhead_measured, check_overhead + eps, float, \"%f\");\n\t\tBC_ASSERT_GREATER(overhead_measured, check_overhead - eps, float, \"%f\");\n\t}\n\n\t// check for fec param (10, 0)\n\toverhead.reset(1);\n\toverhead_measured = overhead.computeOverheadEstimator();\n\tBC_ASSERT_EQUAL(overhead_measured, 0., float, \"%f\");\n\toverheads.clear();\n\tfor (int i = 0; i < block_nb + 1; i++) {\n\t\tfor (int j = 1; j <= 10; j++) {\n\t\t\toverhead.sendSourcePacket(source_packets_size + j + i, 0);\n\t\t}\n\t\toverhead.sendRepairPacket(repair_packets_size + 10 + i, 0);\n\t\toverhead.encoderFull();\n\t\toverhead_measured = overhead.computeOverheadEstimator();\n\t\trepair_size_sum = static_cast<float>(repair_packets_size + i + 10);\n\t\tsource_size_sum = static_cast<float>(10 * (source_packets_size + i) + 55);\n\t\toverheads.push_back(repair_size_sum / source_size_sum);\n\t\twhile (static_cast<int>(overheads.size()) > block_nb) {\n\t\t\toverheads.erase(overheads.begin());\n\t\t}\n\t\tif (overheads.size() < 5) {\n\t\t\tcheck_overhead = 0.;\n\t\t} else {\n\t\t\tcheck_overhead =\n\t\t\t    std::accumulate(overheads.begin(), overheads.end(), 0.) / static_cast<float>(overheads.size());\n\t\t}\n\t\tBC_ASSERT_LOWER(overhead_measured, check_overhead + eps, float, \"%f\");\n\t\tBC_ASSERT_GREATER(overhead_measured, check_overhead - eps, float, \"%f\");\n\t}\n\n\t// check for fec param (5, 5)\n\toverhead.reset(1);\n\toverhead_measured = overhead.computeOverheadEstimator();\n\tBC_ASSERT_EQUAL(overhead_measured, 0., float, \"%f\");\n\toverheads.clear();\n\tfor (int i = 0; i < block_nb + 1; i++) {\n\t\tfor (int j = 1; j <= 25; j++) {\n\t\t\toverhead.sendSourcePacket(source_packets_size + j + i, 0);\n\t\t}\n\t\toverhead.sendRepairPacket(repair_packets_size + 5 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 10 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 15 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 20 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 25 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 21 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 22 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 23 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 24 + i, 0);\n\t\toverhead.sendRepairPacket(repair_packets_size + 25 + i, 0);\n\t\toverhead.encoderFull();\n\t\toverhead_measured = overhead.computeOverheadEstimator();\n\t\trepair_size_sum = static_cast<float>(10 * (repair_packets_size + i) + 190);\n\t\tsource_size_sum = static_cast<float>(25 * (source_packets_size + i) + 325);\n\t\toverheads.push_back(repair_size_sum / source_size_sum);\n\t\twhile (static_cast<int>(overheads.size()) > block_nb) {\n\t\t\toverheads.erase(overheads.begin());\n\t\t}\n\t\tif (overheads.size() < 5) {\n\t\t\tcheck_overhead = 0.;\n\t\t} else {\n\t\t\tcheck_overhead =\n\t\t\t    std::accumulate(overheads.begin(), overheads.end(), 0.) / static_cast<float>(overheads.size());\n\t\t}\n\t\tBC_ASSERT_LOWER(overhead_measured, check_overhead + eps, float, \"%f\");\n\t\tBC_ASSERT_GREATER(overhead_measured, check_overhead - eps, float, \"%f\");\n\t}\n}\n\nstatic void graph_source_node_test(void) {\n\tuint16_t seqNum = 42;\n\tFecSourceNode sourceNode(seqNum);\n\tstd::set<uint16_t> rowRepairCheck = {0, 10, 20, 30, 40};\n\tstd::set<uint16_t> colRepairCheck = {10, 11, 12, 13, 14};\n\tBC_ASSERT_EQUAL(sourceNode.getSeqNum(), seqNum, uint16_t, \"%u\");\n\tfor (uint16_t i = 0; i < 5; i++) {\n\t\tsourceNode.addRowRepair(i * 10);\n\t\tsourceNode.addColRepair(i + 10);\n\t}\n\tsourceNode.addRowRepair(20);\n\tauto rowRepair = sourceNode.getRowRepair();\n\tauto colRepair = sourceNode.getColRepair();\n\tBC_ASSERT_TRUE(rowRepair == rowRepairCheck);\n\tBC_ASSERT_TRUE(colRepair == colRepairCheck);\n}\n\nstatic void graph_repair_node_test(void) {\n\tstd::vector<uint16_t> sourceSeqNum = {0, 10, 20, 30, 40};\n\tFecRepairNode repairNode(sourceSeqNum);\n\tauto sourceSeqNumTest = repairNode.getProtectedSources();\n\tint i = 0;\n\tBC_ASSERT_EQUAL(sourceSeqNumTest.size(), 5, size_t, \"%zu\");\n\tfor (auto it = sourceSeqNumTest.begin(); it != sourceSeqNumTest.end(); ++it) {\n\t\tBC_ASSERT_EQUAL(*it, sourceSeqNum.at(i), uint16_t, \"%u\");\n\t\ti++;\n\t}\n}\n\nstatic void graph_packet_connection_test(void) {\n\tFecPacketsConnection packetConnection;\n\n\t// disjoint FEC blocks\n\tpacketConnection.addRowRepair(0, std::vector<uint16_t>{0, 1, 2});\n\tpacketConnection.addColRepair(1, std::vector<uint16_t>{1, 4, 7});\n\tpacketConnection.addRowRepair(3, std::vector<uint16_t>{3, 4, 5});\n\tpacketConnection.addColRepair(16, std::vector<uint16_t>{16, 18});\n\tpacketConnection.addRowRepair(6, std::vector<uint16_t>{6, 7, 8});\n\tpacketConnection.addRowRepair(9, std::vector<uint16_t>{9, 10, 11});\n\tpacketConnection.addRowRepair(12, std::vector<uint16_t>{12, 13, 14});\n\tpacketConnection.addRowRepair(17, std::vector<uint16_t>{17, 18});\n\tpacketConnection.addColRepair(0, std::vector<uint16_t>{0, 3, 6});\n\tpacketConnection.addColRepair(2, std::vector<uint16_t>{2, 5, 8});\n\tpacketConnection.addColRepair(15, std::vector<uint16_t>{15, 17});\n\tpacketConnection.addColRepair(19, std::vector<uint16_t>{19, 21});\n\tpacketConnection.addRowRepair(15, std::vector<uint16_t>{15, 16});\n\tpacketConnection.addColRepair(2000, std::vector<uint16_t>{20, 22});\n\n\tstd::set<uint16_t> repairRowTest;\n\tstd::set<uint16_t> repairColTest;\n\tpacketConnection.getRepairPacketsToRecoverSource(2, repairRowTest, repairColTest);\n\tBC_ASSERT_TRUE(repairRowTest == std::set<uint16_t>({0, 3, 6}));\n\tBC_ASSERT_TRUE(repairColTest == std::set<uint16_t>({0, 1, 2}));\n\n\tpacketConnection.getRepairPacketsToRecoverSource(9, repairRowTest, repairColTest);\n\tBC_ASSERT_TRUE(repairRowTest == std::set<uint16_t>({9}));\n\tBC_ASSERT_TRUE(repairColTest.empty());\n\n\tpacketConnection.getRepairPacketsToRecoverSource(20, repairRowTest, repairColTest);\n\tBC_ASSERT_TRUE(repairRowTest.empty());\n\tBC_ASSERT_TRUE(repairColTest == std::set<uint16_t>({2000}));\n\n\t// overlapped FEC blocks\n\tfor (uint16_t i = 23; i < 42; i++) {\n\t\tuint16_t j = i + 1;\n\t\tpacketConnection.addColRepair(i, std::vector<uint16_t>{i, j});\n\t}\n\tpacketConnection.getRepairPacketsToRecoverSource(23, repairRowTest, repairColTest);\n\tBC_ASSERT_TRUE(repairRowTest.empty());\n\tBC_ASSERT_TRUE(repairColTest == std::set<uint16_t>({23, 24, 25, 26, 27, 28, 29, 30, 31, 32}));\n\n\tpacketConnection.getRepairPacketsToRecoverSource(29, repairRowTest, repairColTest);\n\tBC_ASSERT_TRUE(repairRowTest.empty());\n\tBC_ASSERT_TRUE(repairColTest ==\n\t               std::set<uint16_t>({23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}));\n\n\tpacketConnection.getRepairPacketsToRecoverSource(36, repairRowTest, repairColTest);\n\tBC_ASSERT_TRUE(repairRowTest.empty());\n\tBC_ASSERT_TRUE(repairColTest ==\n\t               std::set<uint16_t>({26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41}));\n\n\t// clean\n\tpacketConnection.cleanRowRepair(3);\n\tpacketConnection.cleanRowRepair(9);\n\tpacketConnection.cleanRowRepair(15);\n\tpacketConnection.cleanRowRepair(17);\n\tpacketConnection.cleanRowRepair(17);\n\tpacketConnection.cleanColRepair(0);\n\tpacketConnection.cleanColRepair(15);\n\tpacketConnection.cleanColRepair(2000);\n\tpacketConnection.cleanColRepair(35);\n\tpacketConnection.cleanColRepair(35);\n\n\tpacketConnection.getRepairPacketsToRecoverSource(2, repairRowTest, repairColTest);\n\tBC_ASSERT_TRUE(repairRowTest == std::set<uint16_t>({0, 6}));\n\tBC_ASSERT_TRUE(repairColTest == std::set<uint16_t>({1, 2}));\n\n\tpacketConnection.getRepairPacketsToRecoverSource(18, repairRowTest, repairColTest);\n\tBC_ASSERT_TRUE(repairRowTest.empty());\n\tBC_ASSERT_TRUE(repairColTest == std::set<uint16_t>({16}));\n\n\tpacketConnection.getRepairPacketsToRecoverSource(23, repairRowTest, repairColTest);\n\tBC_ASSERT_TRUE(repairRowTest.empty());\n\tBC_ASSERT_TRUE(repairColTest == std::set<uint16_t>({23, 24, 25, 26, 27, 28, 29, 30, 31, 32}));\n\n\tpacketConnection.getRepairPacketsToRecoverSource(29, repairRowTest, repairColTest);\n\tBC_ASSERT_TRUE(repairRowTest.empty());\n\tBC_ASSERT_TRUE(repairColTest == std::set<uint16_t>({23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34}));\n\n\tpacketConnection.getRepairPacketsToRecoverSource(36, repairRowTest, repairColTest);\n\tBC_ASSERT_TRUE(repairRowTest.empty());\n\tBC_ASSERT_TRUE(repairColTest == std::set<uint16_t>({36, 37, 38, 39, 40, 41}));\n\n\t// those source packets don't exist any more in FEC graph\n\tfor (auto sourceToRepair : std::vector<uint16_t>{3, 9, 10, 11, 15, 17, 20, 22}) {\n\t\tpacketConnection.getRepairPacketsToRecoverSource(sourceToRepair, repairRowTest, repairColTest);\n\t\tBC_ASSERT_TRUE(repairRowTest.empty());\n\t\tBC_ASSERT_TRUE(repairColTest.empty());\n\t}\n\n\t// reset\n\tpacketConnection.reset();\n\tfor (uint16_t i = 0; i < 42; i++) {\n\t\tpacketConnection.getRepairPacketsToRecoverSource(i, repairRowTest, repairColTest);\n\t\tBC_ASSERT_TRUE(repairRowTest.empty());\n\t\tBC_ASSERT_TRUE(repairColTest.empty());\n\t}\n}\n\nstatic void generate_packets_for_fec_block(std::map<uint16_t, std::shared_ptr<FecSourcePacket>> &generatedSources,\n                                           std::map<uint16_t, std::shared_ptr<FecRepairPacket>> &generatedRepair,\n                                           int L,\n                                           int D,\n                                           bool is2D,\n                                           uint16_t seqNumStartSource,\n                                           uint16_t seqNumStartRepair,\n                                           FecEncoder &encoder,\n                                           RtpSession *session) {\n\n\tencoder.update(L, D, is2D);\n\tencoder.reset(seqNumStartSource);\n\tint nbCol = L;\n\tint nbRow = (!is2D && D == 0) ? 1 : D;\n\tfor (int i = 0; i < nbRow; i++) {\n\t\tfor (int j = 0; j < nbCol; j++) {\n\t\t\tuint16_t seqNum = static_cast<uint16_t>(seqNumStartSource + i * L + j);\n\t\t\tif (generatedSources.count(seqNum) == 1) {\n\t\t\t\tencoder.add(*generatedSources.at(seqNum));\n\t\t\t} else {\n\t\t\t\tmblk_t *packet = newPacketWithLetter(session, seqNum, seqNum * 10, 'a', 100 + i * L + j);\n\t\t\t\tauto source = std::make_shared<FecSourcePacket>(packet);\n\t\t\t\tgeneratedSources.emplace(seqNum, source);\n\t\t\t\tencoder.add(*source);\n\t\t\t}\n\t\t}\n\t}\n\tif (encoder.isRowFull()) {\n\t\tfor (int i = 0; i < nbRow; i++) {\n\t\t\tauto repair = encoder.getRowRepair(i);\n\t\t\trtp_set_seqnumber(repair->getRepairPacket(), seqNumStartRepair);\n\t\t\trtp_set_timestamp(repair->getRepairPacket(),\n\t\t\t                  static_cast<uint32_t>(repair->createSequenceNumberList().back()) * 10);\n\t\t\tgeneratedRepair.emplace(repair->getSeqnum(), repair);\n\t\t\tseqNumStartRepair++;\n\t\t}\n\t}\n\tif (encoder.isColFull()) {\n\t\tfor (int i = 0; i < nbCol; i++) {\n\t\t\tauto repair = encoder.getColRepair(i);\n\t\t\trtp_set_seqnumber(repair->getRepairPacket(), seqNumStartRepair);\n\t\t\trtp_set_timestamp(repair->getRepairPacket(),\n\t\t\t                  static_cast<uint32_t>(repair->createSequenceNumberList().back()) * 10);\n\t\t\tgeneratedRepair.emplace(repair->getSeqnum(), repair);\n\t\t\tseqNumStartRepair++;\n\t\t}\n\t}\n}\n\nstatic void receive_cluster_add_source_test(void) {\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tauto cluster = ReceiveCluster(session, 40);\n\tmblk_t *packet = NULL;\n\n\t// packets received in chronological order\n\tfor (int k = 0; k < 15; k++) {\n\t\tpacket = newPacketWithLetter(session, k, k * 10, 'a' + k, 150 + k);\n\t\tcluster.add(packet);\n\t\tauto sourceAdded = cluster.getSourcePacket(k);\n\t\tBC_ASSERT_PTR_NOT_NULL(sourceAdded);\n\t\tif (sourceAdded) {\n\t\t\tBC_ASSERT_TRUE(packets_are_equals(packet, sourceAdded->getPacket()));\n\t\t}\n\t}\n\tfor (uint16_t i = 0; i < 11; i++) {\n\t\tauto source = cluster.getSourcePacket(i);\n\t\tBC_ASSERT_PTR_NULL(source);\n\t}\n\tfor (uint16_t i = 11; i < 15; i++) {\n\t\tauto source = cluster.getSourcePacket(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(source);\n\t}\n\n\t// packets not received in chronological order\n\tfor (uint16_t i : std::vector<uint16_t>{15, 17, 19, 21, 23}) {\n\t\tpacket = newPacketWithLetter(session, i, i * 10, 'a' + i, 150 + i);\n\t\tcluster.add(packet);\n\t\tauto sourceAdded = cluster.getSourcePacket(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(sourceAdded);\n\t\tif (sourceAdded) {\n\t\t\tBC_ASSERT_TRUE(packets_are_equals(packet, sourceAdded->getPacket()));\n\t\t}\n\t}\n\tfor (uint16_t i : std::vector<uint16_t>{21, 23}) {\n\t\tauto source = cluster.getSourcePacket(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(source);\n\t}\n\tfor (uint16_t i : std::vector<uint16_t>{14, 15, 17, 19}) {\n\t\tauto source = cluster.getSourcePacket(i);\n\t\tBC_ASSERT_PTR_NULL(source);\n\t}\n\tfor (uint16_t i : std::vector<uint16_t>{16, 18, 20, 22, 24}) {\n\t\tpacket = newPacketWithLetter(session, i, i * 10, 'a' + i, 150 + i);\n\t\tcluster.add(packet);\n\t\tauto sourceAdded = cluster.getSourcePacket(i);\n\t\tif (i == 16 or i == 18) {\n\t\t\tBC_ASSERT_PTR_NULL(sourceAdded);\n\t\t} else {\n\t\t\tBC_ASSERT_PTR_NOT_NULL(sourceAdded);\n\t\t}\n\t\tif (sourceAdded) {\n\t\t\tBC_ASSERT_TRUE(packets_are_equals(packet, sourceAdded->getPacket()));\n\t\t}\n\t}\n\tfor (uint16_t i : std::vector<uint16_t>{21, 22, 23, 24}) {\n\t\tauto source = cluster.getSourcePacket(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(source);\n\t}\n\tfor (uint16_t i : std::vector<uint16_t>{16, 18, 20}) {\n\t\tauto source = cluster.getSourcePacket(i);\n\t\tBC_ASSERT_PTR_NULL(source);\n\t}\n\n\t// receive too old packet\n\tpacket = newPacketWithLetter(session, 25, 199, 'a', 150);\n\tcluster.add(packet);\n\tauto sourceAdded = cluster.getSourcePacket(25);\n\tBC_ASSERT_PTR_NULL(sourceAdded);\n\n\trtp_session_destroy(session);\n}\n\nstatic void receive_cluster_add_repair_test(void) {\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tuint16_t seqNumStart = 0;\n\tuint32_t repairWindow = 101;\n\tFecParamsController params(repairWindow);\n\tparams.updateParams(1);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\tReceiveCluster cluster = ReceiveCluster(session, repairWindow);\n\n\t// generate source and repair packets\n\tstd::map<uint16_t, std::shared_ptr<FecSourcePacket>> receivedSource;\n\tstd::map<uint16_t, std::shared_ptr<FecRepairPacket>> receivedRepair;\n\tgenerate_packets_for_fec_block(receivedSource, receivedRepair, 5, 3, true, seqNumStart, seqNumStart, encoder,\n\t                               session);\n\tgenerate_packets_for_fec_block(receivedSource, receivedRepair, 5, 0, false, seqNumStart + 15, 8, encoder, session);\n\tgenerate_packets_for_fec_block(receivedSource, receivedRepair, 5, 0, false, seqNumStart + 20, 9, encoder, session);\n\n\t// receive repair packets out of order\n\tfor (uint16_t i : std::vector<uint16_t>{8, 2, 4, 5, 0, 1, 6, 7, 3, 9}) {\n\t\tcluster.add(receivedRepair.at(i));\n\t}\n\tBC_ASSERT_EQUAL(static_cast<int>(cluster.getRowRepairCpt()), 5, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(cluster.getColRepairCpt()), 5, int, \"%d\");\n\n\t// check which source packet can be repaired\n\tfor (uint16_t i : std::vector<uint16_t>{0, 1, 2, 5, 6, 7, 4, 9, 10, 12, 13}) {\n\t\tcluster.add(receivedSource.at(i)->getPacketCopy());\n\t\tauto source = cluster.getSourcePacket(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(source);\n\t}\n\tstd::set<uint16_t> irrepairableSourceSeqNum = {3, 8};\n\tfor (uint16_t seqNum : std::vector<uint16_t>{3, 8, 11, 14}) {\n\t\tcluster.repair(seqNum);\n\t\tauto repairedSource = cluster.getSourcePacket(seqNum);\n\t\tif (irrepairableSourceSeqNum.count(seqNum) == 1) {\n\t\t\tBC_ASSERT_PTR_NULL(repairedSource);\n\t\t} else {\n\t\t\tBC_ASSERT_PTR_NOT_NULL(repairedSource);\n\t\t\tif (repairedSource) {\n\t\t\t\tauto refSource = receivedSource.at(seqNum);\n\t\t\t\tBC_ASSERT_TRUE(packets_are_equals(repairedSource->getPacket(), refSource->getPacket()));\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (uint16_t i : std::vector<uint16_t>{15, 16, 17}) {\n\t\tcluster.add(receivedSource.at(i)->getPacketCopy());\n\t\tauto source = cluster.getSourcePacket(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(source);\n\t}\n\tfor (uint16_t i = 19; i <= 24; i++) {\n\t\tcluster.add(receivedSource.at(i)->getPacketCopy());\n\t\tauto source = cluster.getSourcePacket(i);\n\t\tBC_ASSERT_PTR_NOT_NULL(source);\n\t}\n\t{\n\t\tuint16_t seqNum = 18;\n\t\tcluster.repair(seqNum);\n\t\tauto repairedSource = cluster.getSourcePacket(seqNum);\n\t\tBC_ASSERT_PTR_NOT_NULL(repairedSource);\n\t\tif (repairedSource) {\n\t\t\tauto refSource = receivedSource.at(seqNum);\n\t\t\tBC_ASSERT_TRUE(packets_are_equals(repairedSource->getPacket(), refSource->getPacket()));\n\t\t}\n\t}\n\t{\n\t\tuint16_t seqNum = 13;\n\t\tcluster.repair(seqNum);\n\t\tauto repairedSource = cluster.getSourcePacket(seqNum);\n\t\tBC_ASSERT_PTR_NULL(repairedSource);\n\t}\n\tBC_ASSERT_EQUAL(static_cast<int>(cluster.getRowRepairCpt()), 5, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(cluster.getColRepairCpt()), 5, int, \"%d\");\n\n\trtp_session_destroy(session);\n}\n\nstatic void receive_cluster_repair_test(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tuint16_t seqNumStart = 5;\n\tFecParamsController params(200000);\n\tparams.updateParams(1);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\n\t// generate source and repair packets\n\tstd::map<uint16_t, std::shared_ptr<FecSourcePacket>> receivedSource;\n\tstd::map<uint16_t, std::shared_ptr<FecRepairPacket>> receivedRepair;\n\tgenerate_packets_for_fec_block(receivedSource, receivedRepair, 5, 3, true, seqNumStart, 0, encoder, session);\n\tgenerate_packets_for_fec_block(receivedSource, receivedRepair, 3, 0, false, 20, 8, encoder, session);\n\tgenerate_packets_for_fec_block(receivedSource, receivedRepair, 3, 0, false, 23, 9, encoder, session);\n\tgenerate_packets_for_fec_block(receivedSource, receivedRepair, 3, 3, false, 26, 10, encoder, session);\n\tgenerate_packets_for_fec_block(receivedSource, receivedRepair, 3, 3, true, 35, 13, encoder, session);\n\tfor (int i = 0; i < 5; i++) {\n\t\tgenerate_packets_for_fec_block(receivedSource, receivedRepair, 1, 2, false, 44 + i, 19 + i, encoder, session);\n\t}\n\n\t// lost packets\n\tstd::map<uint16_t, std::shared_ptr<FecSourcePacket>> missingSource;\n\tstd::set<uint16_t> missingSourceSeqNum = {10, 19, 21, 26, 29, 31, 39, 40, 42, 43, 45, 46};\n\tfor (uint16_t seqNum : missingSourceSeqNum) {\n\t\tmissingSource.emplace(seqNum, receivedSource.at(seqNum));\n\t\treceivedSource.erase(seqNum);\n\t}\n\tstd::map<uint16_t, std::shared_ptr<FecRepairPacket>> missingRepair;\n\tstd::set<uint16_t> missingRepairSeqNum = {1, 9, 11, 16};\n\tfor (uint16_t seqNum : missingRepairSeqNum) {\n\t\tmissingRepair.emplace(seqNum, receivedRepair.at(seqNum));\n\t\treceivedRepair.erase(seqNum);\n\t}\n\n\t// receive packets\n\tauto cluster = ReceiveCluster(session, 200000);\n\tfor (auto sp : receivedSource) {\n\t\tcluster.add(sp.second->getPacketCopy());\n\t}\n\tfor (auto rp : receivedRepair) {\n\t\tcluster.add(rp.second);\n\t}\n\n\t// repair missing packets\n\tstd::set<uint16_t> irrepairableSourceSeqNum = {26, 29, 39, 40, 42, 43};\n\tfor (uint16_t seqNum : missingSourceSeqNum) {\n\t\tcluster.repair(seqNum);\n\t\tauto repairedSource = cluster.getSourcePacket(seqNum);\n\t\tif (irrepairableSourceSeqNum.count(seqNum) == 1) {\n\t\t\tBC_ASSERT_PTR_NULL(repairedSource);\n\t\t} else {\n\t\t\tBC_ASSERT_PTR_NOT_NULL(repairedSource);\n\t\t\tif (repairedSource) {\n\t\t\t\tauto refSource = missingSource.at(seqNum);\n\t\t\t\tBC_ASSERT_TRUE(packets_are_equals(repairedSource->getPacket(), refSource->getPacket()));\n\t\t\t}\n\t\t}\n\t}\n\n\t// repair out-of-scope packet\n\tcluster.repair(2000);\n\tauto notRepairedSource = cluster.getSourcePacket(2000);\n\tBC_ASSERT_PTR_NULL(notRepairedSource);\n\n\t// repair non lost packet\n\tcluster.repair(17);\n\tauto notMissingSource = cluster.getSourcePacket(17);\n\tBC_ASSERT_PTR_NOT_NULL(notMissingSource);\n\tif (notMissingSource) {\n\t\tauto refSource = receivedSource.at(17);\n\t\tBC_ASSERT_TRUE(packets_are_equals(notMissingSource->getPacket(), refSource->getPacket()));\n\t}\n\n\t// add a row repair packet that overlaps another FEC block and make irrepairable packets 39 to 43 repairable\n\t// again\n\tgenerate_packets_for_fec_block(receivedSource, receivedRepair, 5, 0, false, 35, 24, encoder, session);\n\t// receive packets\n\tfor (auto rp : receivedRepair) {\n\t\tcluster.add(rp.second);\n\t}\n\t// repair\n\tstd::set<uint16_t> newRepairableSourceSeqNum = {39, 40, 42, 43};\n\tfor (uint16_t seqNum : newRepairableSourceSeqNum) {\n\t\tcluster.repair(seqNum);\n\t\tauto repairedSource = cluster.getSourcePacket(seqNum);\n\t\tBC_ASSERT_PTR_NOT_NULL(repairedSource);\n\t\tif (repairedSource) {\n\t\t\tauto refSource = missingSource.at(seqNum);\n\t\t\tBC_ASSERT_TRUE(packets_are_equals(repairedSource->getPacket(), refSource->getPacket()));\n\t\t}\n\t}\n\n\trtp_session_destroy(session);\n}\n\nstatic void receive_cluster_reset_test(void) {\n\n\tRtpSession *session = rtp_session_new(RTP_SESSION_SENDRECV);\n\tFecParamsController params(200000);\n\tparams.updateParams(1);\n\tFecEncoder encoder(&params);\n\tencoder.init(session, session);\n\n\tstd::map<uint16_t, std::shared_ptr<FecSourcePacket>> receivedSource;\n\tstd::map<uint16_t, std::shared_ptr<FecRepairPacket>> receivedRepair;\n\tfor (int i = 0; i < 5; i++) {\n\t\tgenerate_packets_for_fec_block(receivedSource, receivedRepair, 3, 3, true, i * 9, i * 6, encoder, session);\n\t}\n\n\tauto cluster = ReceiveCluster(session, 200000);\n\tfor (auto sp : receivedSource) {\n\t\tcluster.add(sp.second->getPacketCopy());\n\t}\n\tfor (auto rp : receivedRepair) {\n\t\tcluster.add(rp.second);\n\t}\n\n\tcluster.reset();\n\tBC_ASSERT_EQUAL(static_cast<int>(cluster.getRowRepairCpt()), 5 * 3, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(cluster.getColRepairCpt()), 5 * 3, int, \"%d\");\n\tfor (auto sp : receivedSource) {\n\t\tBC_ASSERT_PTR_NULL(cluster.getSourcePacket(sp.first));\n\t\tcluster.repair(sp.first);\n\t\tBC_ASSERT_PTR_NULL(cluster.getSourcePacket(sp.first));\n\t}\n\n\trtp_session_destroy(session);\n}\n\nstatic void stats_sent_packets(void) {\n\tFecStreamStats stats;\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getFecStats()->row_repair_sent), 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getFecStats()->col_repair_sent), 0, int, \"%d\");\n\n\tint rowNum = 12;\n\tint colNum = 27;\n\tfor (int i = 0; i < rowNum; i++)\n\t\tstats.rowRepairSent();\n\tfor (int i = 0; i < colNum; i++)\n\t\tstats.colRepairSent();\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getFecStats()->row_repair_sent), rowNum, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getFecStats()->col_repair_sent), colNum, int, \"%d\");\n}\n\nstatic void stats_received_packets(void) {\n\tFecStreamStats stats;\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getFecStats()->row_repair_received), 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getFecStats()->col_repair_received), 0, int, \"%d\");\n\n\tstats.rowRepairReceived(2);\n\tstats.colRepairReceived(3);\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getFecStats()->row_repair_received), 2, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getFecStats()->col_repair_received), 3, int, \"%d\");\n\tstats.rowRepairReceived(7);\n\tstats.colRepairReceived(4);\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getFecStats()->row_repair_received), 7, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getFecStats()->col_repair_received), 4, int, \"%d\");\n}\n\nstatic void stats_count_packets(void) {\n\tFecStreamStats stats;\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getPacketsLost()), 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getPacketsRecovered()), 0, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getPacketsNotRecovered()), 0, int, \"%d\");\n\n\tint16_t diff = 0;\n\tuint16_t new_seqnum_received = 0;\n\tint last_seqnum = 0;\n\n\t// packets 0-18: missing, try to repair\n\tfor (uint16_t i = 0; i < 19; i++) {\n\t\tstats.askedPacket(i);\n\t}\n\t// packets 0-9: missing and repaired at the second try\n\tfor (uint16_t i = 0; i < 10; i++) {\n\t\tstats.askedPacket(i);\n\t\tstats.repairedPacket(i);\n\t}\n\t// packets 10-14: arrive in the jitter buffer\n\t// -> not asked any more\n\t// -> not counted as recovered nor lost\n\t// 10-14\n\t// packets 15-19: definitely lost\n\t// packet 20: read from jitter buffer, oldest missing packets are lost forever\n\tnew_seqnum_received = 20;\n\tlast_seqnum = 14;\n\tdiff = new_seqnum_received - last_seqnum - 1;\n\tlast_seqnum = (int)new_seqnum_received - (int)diff - 1;\n\tstats.definitelyLostPacket(new_seqnum_received, diff);\n\t// packets 21-29: missing, try to repair\n\tfor (uint16_t i = 21; i < 30; i++) {\n\t\tstats.askedPacket(i);\n\t}\n\t// packets 30-39: missing and repaired\n\tfor (uint16_t i = 30; i < 40; i++) {\n\t\tstats.askedPacket(i);\n\t\tstats.repairedPacket(i);\n\t}\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getPacketsLost()), 25, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getPacketsRecovered()), 20, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getPacketsNotRecovered()), 5, int, \"%d\");\n\t// packets 40-69: missing, try to repair\n\tfor (uint16_t i = 40; i < 70; i++) {\n\t\tstats.askedPacket(i);\n\t}\n\t// packets 70-199 skipped, never asked\n\t// packets 201-204: missing, try to repair\n\tfor (uint16_t i = 201; i < 205; i++) {\n\t\tstats.askedPacket(i);\n\t}\n\t// packet 200: read from jitter buffer, oldest missing packets (21-29 and 40-199) are lost forever\n\tnew_seqnum_received = 200;\n\tlast_seqnum = 39;\n\tdiff = new_seqnum_received - last_seqnum - 1;\n\tlast_seqnum = (int)new_seqnum_received - (int)diff - 1;\n\tstats.definitelyLostPacket(new_seqnum_received, diff);\n\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getPacketsLost()), 55, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getPacketsRecovered()), 20, int, \"%d\");\n\tBC_ASSERT_EQUAL(static_cast<int>(stats.getPacketsNotRecovered()), 35, int, \"%d\");\n}\n\nstatic test_t tests[] = {\n\n    TEST_NO_TAG(\"fec parameters update\", fec_params_update_test),\n    TEST_NO_TAG(\"fec parameters level\", fec_params_level_test),\n\n    TEST_NO_TAG(\"bitstring add\", bitstring_add_test),\n    TEST_NO_TAG(\"source_packet_get_payload\", source_packet_get_payload_test),\n    TEST_NO_TAG(\"source_packet_add_payload same size\", source_packet_add_payload_test1),\n    TEST_NO_TAG(\"source_packet_add_payload bigger\", source_packet_add_payload_test2),\n    TEST_NO_TAG(\"source_packet_add_payload smaller\", source_packet_add_payload_test3),\n    TEST_NO_TAG(\"repair_packet_bitstring\", repair_packet_bitstring_test),\n    TEST_NO_TAG(\"repair_packet_add Payload\", repair_packet_add_payload1),\n    TEST_NO_TAG(\"repair packet seqnum list non interleaved\", repair_packet_seqnumListNonInterleaved_test),\n    TEST_NO_TAG(\"repair packet seqnum list interleaved\", repair_packet_seqnumListInterleaved_test),\n\n    TEST_NO_TAG(\"encoder init 1D non interleaved\", encoder_init_1D_non_interleaved_test),\n    TEST_NO_TAG(\"encoder init 1D interleaved\", encoder_init_1D_interleaved_test),\n    TEST_NO_TAG(\"encoder init_2D\", encoder_init_2D_test),\n    TEST_NO_TAG(\"encoder update\", encoder_update_test),\n    TEST_NO_TAG(\"encoder add 1D\", encoder_add_1D_test),\n    TEST_NO_TAG(\"encoder add 1D interleaved\", encoder_add_1D_interleaved_test),\n    TEST_NO_TAG(\"encoder add 2D\", encoder_add_2D_test),\n    TEST_NO_TAG(\"encoder full\", encoder_full_test),\n    TEST_NO_TAG(\"encoder fill\", encoder_fill_test),\n    TEST_NO_TAG(\"encoder reset\", encoder_reset),\n    TEST_NO_TAG(\"encoder clear\", encoder_clear),\n\n    TEST_NO_TAG(\"overhead estimation\", overhead_estimation_test),\n\n    TEST_NO_TAG(\"graph source node\", graph_source_node_test),\n    TEST_NO_TAG(\"graph repair node\", graph_repair_node_test),\n    TEST_NO_TAG(\"graph packet connection\", graph_packet_connection_test),\n\n    TEST_NO_TAG(\"receive cluster add source\", receive_cluster_add_source_test),\n    TEST_NO_TAG(\"receive cluster add repair\", receive_cluster_add_repair_test),\n    TEST_NO_TAG(\"receive cluster repair\", receive_cluster_repair_test),\n    TEST_NO_TAG(\"receive cluster reset\", receive_cluster_reset_test),\n\n    TEST_NO_TAG(\"stats sent packets\", stats_sent_packets),\n    TEST_NO_TAG(\"stats received packets\", stats_received_packets),\n    TEST_NO_TAG(\"stats count packets\", stats_count_packets),\n};\n\ntest_suite_t fec_test_suite = {\n    \"FEC\",                            // Name of test suite\n    NULL,                             // Before all callback\n    NULL,                             // After all callback\n    NULL,                             // Before each callback\n    NULL,                             // After each callback\n    sizeof(tests) / sizeof(tests[0]), // Size of test table\n    tests,                            // Table of test suite\n    0                                 // Average execution time\n};\n"
  },
  {
    "path": "tester/ortp_tester.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"ortp_tester.h\"\n\n#include <bctoolbox/defs.h>\n\n#include \"ortp_tester_utils.h\"\n\nstatic FILE *log_file = NULL;\n\nstatic void log_handler(int lev, const char *fmt, va_list args) {\n#ifdef _WIN32\n\tvfprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, fmt, args);\n\tfprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, \"\\n\");\n#else\n\tva_list cap;\n\tva_copy(cap, args);\n\t/* Otherwise, we must use stdio to avoid log formatting (for autocompletion etc.) */\n\tvfprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, fmt, cap);\n\tfprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, \"\\n\");\n\tva_end(cap);\n#endif\n\n\tif (log_file) {\n\t\tbctbx_logv_out(BCTBX_LOG_DOMAIN, lev, fmt, args);\n\t}\n}\n\nint ortp_tester_set_log_file(const char *filename) {\n\tif (log_file) {\n\t\tfclose(log_file);\n\t}\n\n\tlog_file = fopen(filename, \"w\");\n\tif (!log_file) {\n\t\tbctbx_error(\"Cannot open file [%s] for writing logs because [%s]\", filename, strerror(errno));\n\t\treturn -1;\n\t}\n\n\tbctbx_message(\"Redirecting traces to file [%s]\", filename);\n#if defined(__clang__) || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4)\n#pragma GCC diagnostic push\n#endif\n#if defined(__clang__) || defined(__GNUC__)\n#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\n#endif\n#ifdef _MSC_VER\n#pragma deprecated(message_state_changed_cb)\n#endif\n\tbctbx_set_log_file(log_file);\n#if defined(__clang__) || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4)\n#pragma GCC diagnostic pop\n#endif\n\n\treturn 0;\n}\n\nint silent_arg_func(BCTBX_UNUSED(const char *arg)) {\n\tbctbx_set_log_level(\"ortp\", BCTBX_LOG_ERROR);\n\tbctbx_set_log_level(BCTBX_LOG_DOMAIN, BCTBX_LOG_ERROR);\n\treturn 0;\n}\n\nint verbose_arg_func(BCTBX_UNUSED(const char *arg)) {\n\tbctbx_set_log_level(\"ortp\", BCTBX_LOG_DEBUG);\n\tbctbx_set_log_level(BCTBX_LOG_DOMAIN, BCTBX_LOG_DEBUG);\n\treturn 0;\n}\n\nint logfile_arg_func(const char *arg) {\n\tif (ortp_tester_set_log_file(arg) < 0) return -2;\n\treturn 0;\n}\n\nvoid ortp_tester_init(void (*ftester_printf)(int level, const char *fmt, va_list args)) {\n\tbc_tester_set_silent_func(silent_arg_func);\n\tbc_tester_set_verbose_func(verbose_arg_func);\n\tbc_tester_set_logfile_func(logfile_arg_func);\n\tif (ftester_printf == NULL) ftester_printf = log_handler;\n\tbc_tester_init(ftester_printf, BCTBX_LOG_MESSAGE, BCTBX_LOG_ERROR, \"raw\");\n\n\tbc_tester_add_suite(&extension_header_test_suite);\n\tbc_tester_add_suite(&fec_test_suite);\n\tbc_tester_add_suite(&rtp_test_suite);\n\tbc_tester_add_suite(&bundle_test_suite);\n}\n\nvoid ortp_tester_uninit(void) {\n\tbc_tester_uninit();\n}\n\n#if defined(_WIN32) && !defined(MS2_WINDOWS_DESKTOP)\n#define BUILD_ENTRY_POINT 0\n#else\n#define BUILD_ENTRY_POINT 1\n#endif\n\n#if BUILD_ENTRY_POINT\nint main(int argc, char *argv[]) {\n\tint i, ret;\n\n\tsilent_arg_func(NULL);\n\tortp_tester_init(NULL);\n\n#ifdef HAVE_CONFIG_H\n\t// If the tester is not installed we configure it, so it can be launched without installing\n\tif (!ortp_tester_is_executable_installed(argv[0], \"raw/h265-iframe\")) {\n\t\tbc_tester_set_resource_dir_prefix(ORTP_LOCAL_RESOURCE_LOCATION);\n\t\tprintf(\"Resource dir set to %s\\n\", ORTP_LOCAL_RESOURCE_LOCATION);\n\t}\n#endif\n\n\tfor (i = 1; i < argc; ++i) {\n\t\tret = bc_tester_parse_args(argc, argv, i);\n\t\tif (ret > 0) {\n\t\t\ti += ret - 1;\n\t\t\tcontinue;\n\t\t} else if (ret < 0) {\n\t\t\tbc_tester_helper(argv[0], \"\");\n\t\t}\n\t\treturn ret;\n\t}\n\n\tbctbx_set_log_level(NULL, BCTBX_LOG_DEBUG);\n\n\tret = bc_tester_start(argv[0]);\n\tortp_tester_uninit();\n\treturn ret;\n}\n#endif\n"
  },
  {
    "path": "tester/ortp_tester.h",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef _ORTP_TESTER_H\n#define _ORTP_TESTER_H\n\n#include <bctoolbox/tester.h>\n\n#include <ortp/ortp.h>\n\n#ifdef HAVE_CONFIG_H\n#include \"ortp-config.h\"\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern test_suite_t extension_header_test_suite;\nextern test_suite_t fec_test_suite;\nextern test_suite_t rtp_test_suite;\nextern test_suite_t bundle_test_suite;\n\nvoid ortp_tester_init(void (*ftester_printf)(int level, const char *fmt, va_list args));\nvoid ortp_tester_uninit(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "tester/ortp_tester_utils.cc",
    "content": "/*\n * Copyright (c) 2010-2023 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"ortp_tester_utils.h\"\n\n#include <string>\n\n#include <bctoolbox/utils.hh>\n\n#include \"rtpsession_priv.h\"\n\nbool_t ortp_tester_is_executable_installed(const char *executable, const char *resource) {\n\treturn bctoolbox::Utils::isExecutableInstalled(std::string(executable), std::string(resource));\n}\n\nmblk_t *ortp_tester_make_dummy_rtcp_fb_pli(RtpSession *session) {\n\tint size = sizeof(rtcp_common_header_t) + sizeof(rtcp_fb_header_t);\n\tmblk_t *h = allocb(size, 0);\n\trtcp_common_header_t *ch;\n\trtcp_fb_header_t *fbh;\n\n\t/* Fill PLI */\n\tch = (rtcp_common_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_common_header_t);\n\tfbh = (rtcp_fb_header_t *)h->b_wptr;\n\th->b_wptr += sizeof(rtcp_fb_header_t);\n\tfbh->packet_sender_ssrc = htonl(session->rcv.ssrc);\n\tfbh->media_source_ssrc = htonl(session->snd.ssrc);\n\n\t/* Fill common header */\n\trtcp_common_header_init(ch, session, RTCP_PSFB, RTCP_PSFB_PLI, msgdsize(h));\n\n\treturn h;\n}\n\nstatic size_t rtcp_sr_init(RtpSession *session, uint8_t *buf, size_t size) {\n\trtcp_sr_t *sr = (rtcp_sr_t *)buf;\n\tsize_t sr_size = sizeof(rtcp_sr_t) - sizeof(report_block_t);\n\tif (size < sr_size) return 0;\n\trtcp_common_header_init(&sr->ch, session, RTCP_SR, 0, sr_size);\n\tsr->ssrc = htonl(session->rcv.ssrc);\n\treturn sr_size;\n}\n\nmblk_t *ortp_tester_make_dummy_sr(RtpSession *session) {\n\tmblk_t *sr = allocb(sizeof(rtcp_sr_t), 0);\n\tsr->b_wptr += rtcp_sr_init(session, sr->b_wptr, sizeof(rtcp_sr_t));\n\n\t// Change snd.ssrc so the wanted ssrc to test is set without touching the SDES packet\n\tconst uint32_t snd = session->snd.ssrc;\n\tsession->snd.ssrc = session->rcv.ssrc;\n\n\tmblk_t *sdes = rtp_session_create_rtcp_sdes_packet(session, FALSE);\n\tconcatb(sr, sdes);\n\n\tsession->snd.ssrc = snd;\n\n\tmblk_t *fb_pli = ortp_tester_make_dummy_rtcp_fb_pli(session);\n\tconcatb(sr, fb_pli);\n\n\treturn sr;\n}"
  },
  {
    "path": "tester/ortp_tester_utils.h",
    "content": "/*\n * Copyright (c) 2010-2023 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef ORTP_TESTER_UTILS_H\n#define ORTP_TESTER_UTILS_H\n\n#include \"ortp/ortp.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nbool_t ortp_tester_is_executable_installed(const char *executable, const char *resource);\n\nmblk_t *ortp_tester_make_dummy_rtcp_fb_pli(RtpSession *session);\nmblk_t *ortp_tester_make_dummy_sr(RtpSession *session);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // ORTP_TESTER_UTILS_H\n"
  },
  {
    "path": "tester/rtp_tester.c",
    "content": "/*\n * Copyright (c) 2010-2022 Belledonne Communications SARL.\n *\n * This file is part of oRTP\n * (see https://gitlab.linphone.org/BC/public/ortp).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"ortp_tester.h\"\n#include <ortp/ortp.h>\n\nstatic int tester_before_all(void) {\n\tortp_init();\n\n\treturn 0;\n}\n\nstatic int tester_after_all(void) {\n\tortp_exit();\n\n\treturn 0;\n}\n\nstatic void send_packets_through_tranfer_session(void) {\n\tRtpSession *session;\n\tRtpSession *transfer_session;\n\tint rtp_port, rtcp_port;\n\tFILE *infile;\n\tunsigned char buffer[160];\n\tuint32_t user_ts = 0;\n\tsize_t len = 0;\n\tbool_t error = FALSE;\n\n\tchar *filepath = bc_tester_res(\"raw/h265-iframe\");\n\n\tinfile = fopen(filepath, \"rb\");\n\n\tif (!BC_ASSERT_PTR_NOT_NULL(infile)) {\n\t\tif (filepath) bctbx_free(filepath);\n\t\treturn;\n\t}\n\n\t// Create the default session\n\tsession = rtp_session_new(RTP_SESSION_SENDRECV);\n\n\trtp_session_set_connected_mode(session, TRUE);\n\trtp_session_set_local_addr(session, \"127.0.0.1\", -1, -1);\n\trtp_session_set_payload_type(session, 0);\n\trtp_session_enable_jitter_buffer(session, FALSE);\n\n\t// Create the session that will be used to transfer the packets\n\ttransfer_session = rtp_session_new(RTP_SESSION_SENDRECV);\n\n\trtp_session_set_connected_mode(transfer_session, TRUE);\n\trtp_session_set_local_addr(transfer_session, \"127.0.0.1\", -1, -1);\n\trtp_session_enable_transfer_mode(transfer_session, TRUE);\n\n\t// Connect the two sessions\n\trtp_port = rtp_session_get_local_port(transfer_session);\n\trtcp_port = rtp_session_get_local_rtcp_port(transfer_session);\n\trtp_session_set_remote_addr_full(session, \"127.0.0.1\", rtp_port, \"127.0.0.1\", rtcp_port);\n\n\trtp_port = rtp_session_get_local_port(session);\n\trtcp_port = rtp_session_get_local_rtcp_port(session);\n\trtp_session_set_remote_addr_full(transfer_session, \"127.0.0.1\", rtp_port, \"127.0.0.1\", rtcp_port);\n\n\twhile (((len = fread(buffer, 1, 160, infile)) > 0) && !error) {\n\t\tmblk_t *transfered_packet;\n\t\tmblk_t *received_packet;\n\t\tint size = 0;\n\t\tint cpt = 0;\n\n\t\t// Send a packet through the \"normal\" session and retrieve it with the transfer session\n\t\tmblk_t *sent_packet = rtp_session_create_packet_header(\n\t\t    session, len); // make a non fragmented packet, so ask for len bytes allocated after the header.\n\t\t                   // This is needed to be able to compare directly the received packet with the sent one\n\t\tmemcpy(sent_packet->b_wptr, (uint8_t *)buffer, len);\n\t\tsent_packet->b_wptr += len;\n\n\t\tsize = rtp_session_sendm_with_ts(session, copymsg(sent_packet), user_ts);\n\t\tBC_ASSERT_GREATER(size, 0, int, \"%d\");\n\t\tdo {\n\t\t\tbctbx_sleep_ms(1);\n\t\t\ttransfered_packet = rtp_session_recvm_with_ts(transfer_session, user_ts);\n\t\t\tcpt++;\n\t\t} while (transfered_packet == NULL && cpt < 10);\n\t\tif (!BC_ASSERT_PTR_NOT_NULL(transfered_packet)) {\n\t\t\terror = TRUE;\n\t\t} else {\n\t\t\t// We cannot compare bytes by bytes here as sent_packet has been modified by rtp_session_sendm_with_ts\n\t\t\t// before sending So we check the parts that this function didn't change which is everything but timestamp\n\t\t\tBC_ASSERT_EQUAL(rtp_get_version(transfered_packet), rtp_get_version(sent_packet), uint16_t, \"%hu\");\n\t\t\tBC_ASSERT_EQUAL(rtp_get_padbit(transfered_packet), rtp_get_padbit(sent_packet), uint16_t, \"%hu\");\n\t\t\tBC_ASSERT_EQUAL(rtp_get_markbit(transfered_packet), rtp_get_markbit(sent_packet), uint16_t, \"%hu\");\n\t\t\tBC_ASSERT_EQUAL(rtp_get_extbit(transfered_packet), rtp_get_extbit(sent_packet), uint16_t, \"%hu\");\n\t\t\tBC_ASSERT_TRUE(\n\t\t\t    rtp_get_seqnumber(transfered_packet) ==\n\t\t\t    rtp_get_seqnumber(sent_packet)); // BC_ASSERT_EQUAL here doesn't want to compile on some platforms\n\t\t\tBC_ASSERT_EQUAL(rtp_get_payload_type(transfered_packet), rtp_get_payload_type(sent_packet), uint16_t,\n\t\t\t                \"%hu\");\n\t\t\tBC_ASSERT_TRUE(rtp_get_ssrc(transfered_packet) == rtp_get_ssrc(sent_packet)); // Same here\n\t\t\tBC_ASSERT_EQUAL(rtp_get_cc(transfered_packet), rtp_get_cc(sent_packet), uint16_t, \"%hu\");\n\t\t\tBC_ASSERT_EQUAL(memcmp(transfered_packet->b_rptr + RTP_FIXED_HEADER_SIZE,\n\t\t\t                       sent_packet->b_rptr + RTP_FIXED_HEADER_SIZE,\n\t\t\t                       msgdsize(transfered_packet) - RTP_FIXED_HEADER_SIZE),\n\t\t\t                0, int, \"%d\");\n\n\t\t\t// Send it again via the transfer session and retrieve it with the \"normal\" session\n\t\t\tsize = rtp_session_sendm_with_ts(transfer_session, copymsg(transfered_packet), user_ts);\n\t\t\tBC_ASSERT_GREATER(size, 0, int, \"%d\");\n\t\t\tcpt = 0;\n\t\t\tdo {\n\t\t\t\tbctbx_sleep_ms(1);\n\t\t\t\treceived_packet = rtp_session_recvm_with_ts(session, user_ts);\n\t\t\t\tcpt++;\n\t\t\t} while (transfered_packet == NULL && cpt < 10);\n\t\t\tif (!BC_ASSERT_PTR_NOT_NULL(received_packet)) {\n\t\t\t\terror = TRUE;\n\t\t\t} else {\n\t\t\t\t// Check that the packet received is the same as the transfered one as the \"transfer\" session shouldn't\n\t\t\t\t// modify it's content\n\t\t\t\tBC_ASSERT_EQUAL(memcmp(received_packet->b_rptr, transfered_packet->b_rptr, msgdsize(received_packet)),\n\t\t\t\t                0, int, \"%d\");\n\n\t\t\t\tfreemsg(received_packet);\n\t\t\t}\n\n\t\t\tfreemsg(transfered_packet);\n\t\t}\n\n\t\tfreemsg(sent_packet);\n\n\t\tuser_ts += 160;\n\t}\n\n\tfclose(infile);\n\n\tif (filepath) bctbx_free(filepath);\n\trtp_session_destroy(session);\n\trtp_session_destroy(transfer_session);\n}\n\nstatic void change_remote_address(void) {\n\tRtpSession *conference;\n\tRtpSession *flore;\n\tmblk_t *received_packet;\n\tmblk_t *sent_packet;\n\tint size = 0, cpt = 0;\n\tint rtp_port, rtcp_port;\n\tunsigned char *buffer = ortp_malloc0(160 * sizeof(unsigned char));\n\tuint32_t user_ts = 0;\n\tsize_t len = 160;\n\n\t// Create the conference's session\n\tconference = rtp_session_new(RTP_SESSION_SENDRECV);\n\n\trtp_session_set_local_addr(conference, \"127.0.0.1\", -1, -1);\n\trtp_session_set_payload_type(conference, 0);\n\trtp_session_enable_jitter_buffer(conference, FALSE);\n\trtp_session_set_symmetric_rtp(conference, TRUE);\n\n\t// Create the flore's session\n\tflore = rtp_session_new(RTP_SESSION_SENDRECV);\n\n\trtp_session_set_local_addr(flore, \"127.0.0.1\", -1, -1);\n\trtp_session_set_payload_type(flore, 0);\n\trtp_session_enable_jitter_buffer(flore, FALSE);\n\trtp_session_set_symmetric_rtp(flore, TRUE);\n\n\t// Connect the two sessions\n\trtp_port = rtp_session_get_local_port(conference);\n\trtcp_port = rtp_session_get_local_rtcp_port(conference);\n\trtp_session_set_remote_addr_full(flore, \"127.0.0.1\", rtp_port, \"127.0.0.1\", rtcp_port);\n\n\trtp_session_set_remote_addr_full(conference, \"127.0.0.1\", 1, \"127.0.0.1\", 1);\n\n\t// The conference sends a packet to a bad address\n\tsent_packet = rtp_session_create_packet_header(conference, len);\n\tmemcpy(sent_packet->b_wptr, (uint8_t *)buffer, len);\n\tsent_packet->b_wptr += len;\n\n\tsize = rtp_session_sendm_with_ts(conference, copymsg(sent_packet), user_ts);\n\tBC_ASSERT_GREATER(size, 0, int, \"%d\");\n\n\tfor (cpt = 0, received_packet = NULL; received_packet == NULL && cpt < 10; cpt++) {\n\t\tbctbx_sleep_ms(1);\n\t\treceived_packet = rtp_session_recvm_with_ts(flore, user_ts);\n\t}\n\tBC_ASSERT_PTR_NULL(received_packet);\n\n\tfreemsg(received_packet);\n\tfreemsg(sent_packet);\n\n\tuser_ts += 160;\n\n\t// Flore sends a packet to the conference\n\tsent_packet = rtp_session_create_packet_header(flore, len);\n\tmemcpy(sent_packet->b_wptr, (uint8_t *)buffer, len);\n\tsent_packet->b_wptr += len;\n\n\tsize = rtp_session_sendm_with_ts(flore, copymsg(sent_packet), user_ts);\n\tBC_ASSERT_GREATER(size, 0, int, \"%d\");\n\n\tfor (cpt = 0, received_packet = NULL; received_packet == NULL && cpt < 10; cpt++) {\n\t\tbctbx_sleep_ms(1);\n\t\treceived_packet = rtp_session_recvm_with_ts(conference, user_ts);\n\t}\n\tBC_ASSERT_PTR_NOT_NULL(received_packet);\n\n\tfreemsg(received_packet);\n\tfreemsg(sent_packet);\n\n\tuser_ts += 160;\n\n\t// The conference sends a packet to the adapted address of Flore\n\tsent_packet = rtp_session_create_packet_header(conference, len);\n\tmemcpy(sent_packet->b_wptr, (uint8_t *)buffer, len);\n\tsent_packet->b_wptr += len;\n\n\tsize = rtp_session_sendm_with_ts(conference, copymsg(sent_packet), user_ts);\n\tBC_ASSERT_GREATER(size, 0, int, \"%d\");\n\n\tfor (cpt = 0, received_packet = NULL; received_packet == NULL && cpt < 10; cpt++) {\n\t\tbctbx_sleep_ms(1);\n\t\treceived_packet = rtp_session_recvm_with_ts(flore, user_ts);\n\t}\n\tBC_ASSERT_PTR_NOT_NULL(received_packet);\n\n\tfreemsg(received_packet);\n\tfreemsg(sent_packet);\n\n\tuser_ts += 160;\n\n\t// Flore sends a RE-INVITE with the same bad address like at the beginning\n\trtp_session_set_remote_addr_full(conference, \"127.0.0.1\", 1, \"127.0.0.1\", 1);\n\n\t// The conference sends a packet to the adapted address of Flore\n\tsent_packet = rtp_session_create_packet_header(conference, len);\n\tmemcpy(sent_packet->b_wptr, (uint8_t *)buffer, len);\n\tsent_packet->b_wptr += len;\n\n\tsize = rtp_session_sendm_with_ts(conference, copymsg(sent_packet), user_ts);\n\tBC_ASSERT_GREATER(size, 0, int, \"%d\");\n\n\tfor (cpt = 0, received_packet = NULL; received_packet == NULL && cpt < 10; cpt++) {\n\t\tbctbx_sleep_ms(1);\n\t\treceived_packet = rtp_session_recvm_with_ts(flore, user_ts);\n\t}\n\tBC_ASSERT_PTR_NOT_NULL(received_packet);\n\n\tfreemsg(received_packet);\n\tfreemsg(sent_packet);\n\n\tuser_ts += 160;\n\n\tfor (int i = 0; i < 2; i++) {\n\t\t// Flore address change\n\t\trtp_session_set_local_addr(flore, \"127.0.0.1\", -1, -1);\n\t\trtp_port = rtp_session_get_local_port(conference);\n\t\trtcp_port = rtp_session_get_local_rtcp_port(conference);\n\t\trtp_session_set_remote_addr_full(flore, \"127.0.0.1\", rtp_port, \"127.0.0.1\", rtcp_port);\n\n\t\t// Flore sends a packet to the conference with her new address\n\t\tsent_packet = rtp_session_create_packet_header(flore, len);\n\t\tmemcpy(sent_packet->b_wptr, (uint8_t *)buffer, len);\n\t\tsent_packet->b_wptr += len;\n\n\t\tsize = rtp_session_sendm_with_ts(flore, copymsg(sent_packet), user_ts);\n\t\tBC_ASSERT_GREATER(size, 0, int, \"%d\");\n\n\t\tfor (cpt = 0, received_packet = NULL; received_packet == NULL && cpt < 10; cpt++) {\n\t\t\tbctbx_sleep_ms(1);\n\t\t\treceived_packet = rtp_session_recvm_with_ts(conference, user_ts);\n\t\t}\n\t\tBC_ASSERT_PTR_NOT_NULL(received_packet);\n\n\t\tfreemsg(received_packet);\n\t\tfreemsg(sent_packet);\n\n\t\tuser_ts += 160;\n\n\t\t// The conference sends a packet to Flore\n\t\tsent_packet = rtp_session_create_packet_header(conference, len);\n\t\tmemcpy(sent_packet->b_wptr, (uint8_t *)buffer, len);\n\t\tsent_packet->b_wptr += len;\n\n\t\tsize = rtp_session_sendm_with_ts(conference, copymsg(sent_packet), user_ts);\n\t\tBC_ASSERT_GREATER(size, 0, int, \"%d\");\n\n\t\tfor (cpt = 0, received_packet = NULL; received_packet == NULL && cpt < 10; cpt++) {\n\t\t\tbctbx_sleep_ms(1);\n\t\t\treceived_packet = rtp_session_recvm_with_ts(flore, user_ts);\n\t\t}\n\t\tif (i == 0) {\n\t\t\tBC_ASSERT_PTR_NOT_NULL(received_packet);\n\t\t} else {\n\t\t\tBC_ASSERT_PTR_NULL(received_packet);\n\t\t}\n\n\t\tfreemsg(received_packet);\n\t\tfreemsg(sent_packet);\n\n\t\tuser_ts += 160;\n\t}\n\n\tortp_free(buffer);\n\trtp_session_destroy(conference);\n\trtp_session_destroy(flore);\n}\n\nstatic test_t tests[] = {TEST_NO_TAG(\"Send packets through a transfer session\", send_packets_through_tranfer_session),\n                         TEST_NO_TAG(\"Change remote address\", change_remote_address)};\n\ntest_suite_t rtp_test_suite = {\n    \"Rtp\",                            // Name of test suite\n    tester_before_all,                // Before all callback\n    tester_after_all,                 // After all callback\n    NULL,                             // Before each callback\n    NULL,                             // After each callback\n    sizeof(tests) / sizeof(tests[0]), // Size of test table\n    tests                             // Table of test suite\n};\n"
  }
]