├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── actions │ ├── debpkg-bookworm │ │ ├── Dockerfile │ │ ├── action.yml │ │ └── entrypoint.sh │ └── debpkg-sid │ │ ├── Dockerfile │ │ ├── action.yml │ │ └── entrypoint.sh └── workflows │ ├── coverity.yml │ ├── debpkg.yml │ ├── shellcheck.yml │ └── unit-tests.yml ├── .gitignore ├── .gitreview ├── .mailmap ├── .readthedocs.yaml ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── daemon ├── .gitignore ├── .ycm_extra_conf.py ├── Makefile ├── arena.c ├── audio_player.c ├── call.c ├── call_interfaces.c ├── cdr.c ├── cli.c ├── codec.c ├── control_ng.c ├── control_ng_flags_parser.c ├── control_tcp.c ├── control_udp.c ├── cookie_cache.c ├── crypto.c ├── dtls.c ├── dtmf.c ├── graphite.c ├── helpers.c ├── homer.c ├── ice.c ├── iptables.c ├── janus.c ├── jitter_buffer.c ├── kernel.c ├── load.c ├── log.c ├── log.h ├── log_funcs.h ├── loglevels.h ├── main.c ├── media_player.c ├── media_socket.c ├── mqtt.c ├── nftables.c ├── ng_client.c ├── recording.c ├── redis.c ├── rtcp.c ├── rtp.c ├── sdp.c ├── ssrc.c ├── statistics.c ├── stun.c ├── t38.c ├── tcp_listener.c ├── timerthread.c ├── udp_listener.c └── websocket.c ├── debian ├── .gitignore ├── changelog ├── clean ├── control ├── copyright ├── generate-systemd-templates.sh ├── ngcp-rtpengine-daemon.default ├── ngcp-rtpengine-daemon.dirs ├── ngcp-rtpengine-daemon.init ├── ngcp-rtpengine-daemon.install ├── ngcp-rtpengine-daemon.links ├── ngcp-rtpengine-daemon.manpages ├── ngcp-rtpengine-daemon.postinst ├── ngcp-rtpengine-daemon.postrm ├── ngcp-rtpengine-daemon.service ├── ngcp-rtpengine-dev.install ├── ngcp-rtpengine-kernel-dkms.dkms ├── ngcp-rtpengine-kernel-dkms.install ├── ngcp-rtpengine-kernel-dkms.postinst ├── ngcp-rtpengine-kernel-dkms.prerm ├── ngcp-rtpengine-perftest-data.install ├── ngcp-rtpengine-perftest.install ├── ngcp-rtpengine-recording-daemon.default ├── ngcp-rtpengine-recording-daemon.dirs ├── ngcp-rtpengine-recording-daemon.init ├── ngcp-rtpengine-recording-daemon.install ├── ngcp-rtpengine-recording-daemon.links ├── ngcp-rtpengine-recording-daemon.manpages ├── ngcp-rtpengine-recording-daemon.ngcp-rtpengine-recording-nfs-mount.service ├── ngcp-rtpengine-recording-daemon.postinst ├── ngcp-rtpengine-recording-daemon.service ├── ngcp-rtpengine-recording-nfs-setup ├── ngcp-rtpengine-recording-nfs-setup.1 ├── ngcp-rtpengine-utils.install ├── ngcp-rtpengine-utils.manpages ├── ngcp-rtpengine.docs ├── rules └── source │ ├── format │ ├── lintian-overrides │ └── options ├── docs ├── Makefile ├── architecture.md ├── call_recording.md ├── compiling_and_installing.md ├── conf.py ├── glossary.md ├── http_websocket_support.md ├── images │ ├── pic1_rtp_engine_architecture.png │ ├── pic2_rtp_engine_architecture.png │ ├── pic3_media_socket.png │ └── pic4_flags_parsing.png ├── index.rst ├── janus_interface_and_replacement.md ├── make.bat ├── ng_control_protocol.md ├── overview.md ├── requirements.txt ├── rtpengine-recording.md ├── rtpengine.md ├── tcpng_control_protocol.md ├── tests.md ├── transcoding.md ├── troubleshooting.md └── usage.md ├── el ├── README.md ├── build-with-mock.sh ├── rtpengine-8-x86_64.cfg ├── rtpengine-9-x86_64.cfg ├── rtpengine-recording.init ├── rtpengine-recording.service ├── rtpengine-recording.sysconfig ├── rtpengine.init ├── rtpengine.service ├── rtpengine.spec └── rtpengine.sysconfig ├── etc ├── rtpengine-recording.conf └── rtpengine.conf ├── evs ├── etsi-ts-126.442-3gpp-ts-26.442-v17.0.0 │ └── patch └── etsi-ts-126.443-3gpp-ts-26.443-v17.0.0 │ └── patch ├── fixtures ├── LICENSE ├── opus.1.8k.11k.music.ogg ├── opus.1.8k.11k.speech.ogg ├── opus.2.48k.96k.music.ogg ├── opus.2.48k.96k.speech.ogg ├── pcma.1.8k.raw └── pcmu.1.8k.raw ├── include ├── .ycm_extra_conf.py ├── arena.h ├── audio_player.h ├── call.h ├── call_interfaces.h ├── cdr.h ├── cli.h ├── codec.h ├── control_ng.h ├── control_ng_flags_parser.h ├── control_tcp.h ├── control_udp.h ├── cookie_cache.h ├── counter_stats_fields.inc ├── crypto.h ├── dtls.h ├── dtmf.h ├── gauge_stats_fields.inc ├── graphite.h ├── homer.h ├── ice.h ├── iptables.h ├── janus.h ├── jitter_buffer.h ├── kernel.h ├── load.h ├── main.h ├── media_player.h ├── media_socket.h ├── mqtt.h ├── nftables.h ├── ng_client.h ├── recording.h ├── redis.h ├── rtcp.h ├── rtp.h ├── sampled_stats_fields.inc ├── sdp.h ├── ssrc.h ├── statistics.h ├── stun.h ├── t38.h ├── tcp_listener.h ├── timerthread.h ├── types.h ├── udp_listener.h └── websocket.h ├── kernel-module ├── .gitignore ├── Makefile ├── common_counter_stats_fields.inc ├── common_stats.h ├── gen-rtpengine-kmod-flags ├── interface_counter_stats_fields.inc ├── interface_counter_stats_fields_dir.inc ├── interface_sampled_stats_fields.inc ├── kernel_counter_stats_fields.inc ├── other_counter_stats_fields.inc ├── xt_RTPENGINE.c ├── xt_RTPENGINE.h └── xt_RTPENGINE.modules.load.d ├── lib ├── .gitignore ├── .ycm_extra_conf.py ├── Makefile ├── auxlib.c ├── auxlib.h ├── bencode.c ├── bencode.h ├── bitstr.h ├── bufferpool.c ├── bufferpool.h ├── codeclib.c ├── codeclib.h ├── common.Makefile ├── compat.h ├── containers.h ├── deps.Makefile ├── dtmf_rx_fillin-01.h ├── dtmf_rx_fillin-02.h ├── dtmf_rx_fillin-test.c ├── dtmflib.c ├── dtmflib.h ├── fix_frame_channel_layout-01.h ├── fix_frame_channel_layout-02.h ├── fix_frame_channel_layout-03.h ├── fix_frame_channel_layout-04.h ├── fix_frame_channel_layout-05.h ├── fix_frame_channel_layout-test.c ├── helpers.h ├── lib.Makefile ├── loglib.c ├── loglib.h ├── mix_buffer.c ├── mix_buffer.h ├── mix_in_x64_avx2.S ├── mix_in_x64_avx512bw.S ├── mix_in_x64_sse2.S ├── mvr2s_x64_avx2.S ├── mvr2s_x64_avx512.S ├── obj.h ├── poller.c ├── poller.h ├── resample.c ├── resample.h ├── rtcplib.h ├── rtplib.c ├── rtplib.h ├── socket.c ├── socket.h ├── spandsp_logging-01.h ├── spandsp_logging-02.h ├── spandsp_logging-test.c ├── ssllib.c ├── ssllib.h ├── str.c ├── str.h ├── streambuf.c ├── streambuf.h ├── uring.c └── uring.h ├── perf-tester ├── .gitignore ├── .ycm_extra_conf.py ├── Makefile ├── log.c ├── log.h ├── log_funcs.h ├── loglevels.h └── main.c ├── perl └── NGCP │ ├── Rtpclient │ ├── DTLS.pm │ ├── ICE.pm │ ├── RTCP.pm │ ├── RTP.pm │ ├── SDES.pm │ ├── SDP.pm │ └── SRTP.pm │ ├── Rtpengine.pm │ └── Rtpengine │ ├── AutoTest.pm │ └── Test.pm ├── pkg └── deb │ ├── backports │ ├── bookworm │ ├── bullseye │ ├── buster │ ├── focal │ ├── jammy │ ├── noble │ ├── sid │ └── trixie │ ├── generator.sh │ └── old-dkms │ ├── dkms.conf.in │ ├── rtpengine-kernel-dkms.postinst │ └── rtpengine-kernel-dkms.prerm ├── recording-daemon ├── .gitignore ├── .ycm_extra_conf.py ├── Makefile ├── db.c ├── db.h ├── decoder.c ├── decoder.h ├── epoll.c ├── epoll.h ├── forward.c ├── forward.h ├── garbage.c ├── garbage.h ├── inotify.c ├── inotify.h ├── log.c ├── log.h ├── loglevels.h ├── main.c ├── main.h ├── metafile.c ├── metafile.h ├── mix.c ├── mix.h ├── notify.c ├── notify.h ├── output.c ├── output.h ├── packet.c ├── packet.h ├── poller.c ├── poller.h ├── recaux.c ├── recaux.h ├── stream.c ├── stream.h ├── tag.c ├── tag.h └── types.h ├── t ├── .gitignore ├── .ycm_extra_conf.py ├── Makefile ├── aead-aes-crypt.c ├── aead-decrypt.c ├── aes-crypt.c ├── auto-daemon-tests-asymmetric.pl ├── auto-daemon-tests-async-tc.pl ├── auto-daemon-tests-audio-player-play-media.pl ├── auto-daemon-tests-audio-player.pl ├── auto-daemon-tests-codec-prefs.pl ├── auto-daemon-tests-config-file.pl ├── auto-daemon-tests-delay-buffer.pl ├── auto-daemon-tests-delay-timing.pl ├── auto-daemon-tests-dtx-cn.pl ├── auto-daemon-tests-dtx-no-shift.pl ├── auto-daemon-tests-dtx.pl ├── auto-daemon-tests-evs-dtx.pl ├── auto-daemon-tests-evs.pl ├── auto-daemon-tests-heuristic.pl ├── auto-daemon-tests-http.pl ├── auto-daemon-tests-intfs.pl ├── auto-daemon-tests-jb.pl ├── auto-daemon-tests-measure-rtp.pl ├── auto-daemon-tests-moh.pl ├── auto-daemon-tests-mos-fullband.pl ├── auto-daemon-tests-mos-legacy.pl ├── auto-daemon-tests-player-cache.pl ├── auto-daemon-tests-pubsub.pl ├── auto-daemon-tests-redis-json.pl ├── auto-daemon-tests-redis.pl ├── auto-daemon-tests-rtcp.pl ├── auto-daemon-tests-rtpp-flags.pl ├── auto-daemon-tests-sdes-manipulations.pl ├── auto-daemon-tests-sdp-manipulations.pl ├── auto-daemon-tests-sdp-orig-replacements.pl ├── auto-daemon-tests-stats.pl ├── auto-daemon-tests-t38.pl ├── auto-daemon-tests-templ-def-offer.pl ├── auto-daemon-tests-templ-def.pl ├── auto-daemon-tests-transcode-config.pl ├── auto-daemon-tests-transform.pl ├── auto-daemon-tests-websocket.py ├── auto-daemon-tests.pl ├── auto-test-helper ├── log.h ├── log_funcs.h ├── loglevels.h ├── pcm_rtp_test.pl ├── spandsp_fax_pcm_test.pl ├── spandsp_fax_t38_test.pl ├── spandsp_recv_fax_pcm.c ├── spandsp_recv_fax_t38.c ├── spandsp_send_fax_pcm.c ├── spandsp_send_fax_t38.c ├── t38_udptl_test.pl ├── test-amr-decode.c ├── test-amr-encode.c ├── test-basic-ipv4-6.pl ├── test-basic-ipv4.pl ├── test-basic-ipv6-4.pl ├── test-basic-ipv6.pl ├── test-bitstr.c ├── test-const_str_hash.c ├── test-dtls.pl ├── test-dtmf-detect.c ├── test-ice.pl ├── test-interfaces.pl ├── test-kernel-module.c ├── test-mix-buffer.c ├── test-packetloss.pl ├── test-payload-tracker.c ├── test-plain-sdes-full.pl ├── test-plain-sdes.pl ├── test-resample.c ├── test-sdes-plain.pl ├── test-sdes-sdes-select-offer-restrict.pl ├── test-sdes-sdes-select-offer.pl ├── test-sdes-sdes.pl ├── test-stats.c ├── test-transcode.c ├── test-transcode.pl ├── test-unidirectional.pl ├── test.tif ├── test1.conf ├── test2.conf ├── test3.conf ├── test4.conf ├── test5.conf ├── test6.conf ├── tests-preload.c └── time-fudge-preload.c ├── tests ├── .gitignore ├── kernel-module-test.c ├── simulator-ng.pl ├── simulator-tcp.sh ├── simulator-udp.pl ├── stun-client └── stun-server └── utils ├── .gitignore ├── build_test_wrapper ├── const_str_hash ├── gen-bcg729-flags ├── gen-codec-chain-flags ├── gen-common-flags ├── kernel-intercept-dump.pl ├── kernel-intercept-pcap-replay.pl ├── kplay-test.c ├── kplay-test2.c ├── ng-load-tester.py ├── rtpengine-ctl ├── rtpengine-ctl.1 ├── rtpengine-get-table ├── rtpengine-ng-client ├── rtpengine-ng-client.1 ├── srtcp-debug-helper └── srtp-debug-helper /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vi: ts=2 sw=2 et: 3 | 4 | blank_issues_enabled: true 5 | contact_links: 6 | - name: rtpengine mailing list 7 | url: https://rtpengine.com/mailing-list 8 | about: Please ask questions on the mailing list. The issue tracker here is NOT a support forum. 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vi: ts=2 sw=2 et: 3 | 4 | name: Feature request 5 | description: Suggest an improvement 6 | labels: ["feature"] 7 | 8 | body: 9 | - type: markdown 10 | attributes: 11 | value: Thanks for taking the time to fill out this feature request! 12 | 13 | - type: textarea 14 | id: description 15 | attributes: 16 | label: Is your feature request related to a problem? Please describe 17 | description: A clear and concise description of what the problem is. 18 | validations: 19 | required: false 20 | 21 | - type: textarea 22 | id: solution 23 | attributes: 24 | label: Describe the solution you'd like 25 | description: A clear and concise description of what you want to happen. 26 | validations: 27 | required: false 28 | 29 | - type: textarea 30 | id: alternatives 31 | attributes: 32 | label: Describe alternatives you've considered 33 | description: A clear and concise description of any alternative solutions or features you've considered. 34 | validations: 35 | required: false 36 | 37 | - type: input 38 | id: version 39 | attributes: 40 | label: The rtpengine version you checked that didn't have the feature you are asking for 41 | description: If this is not the most recently released upstream version, then please check first if it has that feature already. 42 | placeholder: '12.2.0.0+0~mr12.2.0.0+0~20231205160601.20604+bookworm~1.gbpa75a82' 43 | validations: 44 | required: false 45 | -------------------------------------------------------------------------------- /.github/actions/debpkg-bookworm/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bookworm 2 | 3 | COPY entrypoint.sh /entrypoint.sh 4 | 5 | # avoid "debconf: (TERM is not set, so the dialog frontend is not usable.)" 6 | ENV DEBIAN_FRONTEND noninteractive 7 | 8 | # disable man-db to speed up builds 9 | RUN echo 'man-db man-db/auto-update boolean false' | debconf-set-selections 10 | 11 | RUN apt-get update && apt-get -y install build-essential 12 | 13 | ENTRYPOINT ["/entrypoint.sh"] 14 | -------------------------------------------------------------------------------- /.github/actions/debpkg-bookworm/action.yml: -------------------------------------------------------------------------------- 1 | name: "Build Docker image based on Debian/bookworm" 2 | description: "Build Docker image based on Debian/bookworm" 3 | 4 | runs: 5 | using: 'docker' 6 | image: 'Dockerfile' 7 | -------------------------------------------------------------------------------- /.github/actions/debpkg-bookworm/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eu -o pipefail 3 | 4 | echo "*** Starting execution of '$0' ***" 5 | 6 | echo "** Installing build dependencies **" 7 | apt-get -y build-dep -Ppkg.ngcp-rtpengine.nobcg729 . 8 | 9 | echo "** Building Debian package **" 10 | dpkg-buildpackage -Ppkg.ngcp-rtpengine.nobcg729 11 | 12 | # We're inside /github/workspace/ 13 | echo "** Copying Debian package files to workspace **" 14 | cp ../*.deb ../*.buildinfo ../workspace/ 15 | 16 | echo "*** Finished execution of '$0' ***" 17 | -------------------------------------------------------------------------------- /.github/actions/debpkg-sid/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:sid 2 | 3 | COPY entrypoint.sh /entrypoint.sh 4 | 5 | # avoid "debconf: (TERM is not set, so the dialog frontend is not usable.)" 6 | ENV DEBIAN_FRONTEND noninteractive 7 | 8 | # disable man-db to speed up builds 9 | RUN echo 'man-db man-db/auto-update boolean false' | debconf-set-selections 10 | 11 | RUN apt-get update && apt-get -y install build-essential 12 | 13 | ENTRYPOINT ["/entrypoint.sh"] 14 | -------------------------------------------------------------------------------- /.github/actions/debpkg-sid/action.yml: -------------------------------------------------------------------------------- 1 | name: "Build Docker image based on Debian/sid" 2 | description: "Build Docker image based on Debian/sid" 3 | 4 | runs: 5 | using: 'docker' 6 | image: 'Dockerfile' 7 | -------------------------------------------------------------------------------- /.github/actions/debpkg-sid/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eu -o pipefail 3 | 4 | echo "*** Starting execution of '$0' ***" 5 | 6 | echo "** Installing build dependencies **" 7 | apt-get -y build-dep -Ppkg.ngcp-rtpengine.nobcg729 . 8 | 9 | echo "** Building Debian package **" 10 | dpkg-buildpackage -Ppkg.ngcp-rtpengine.nobcg729 11 | 12 | # We're inside /github/workspace/ 13 | echo "** Copying Debian package files to workspace **" 14 | cp ../*.deb ../*.buildinfo ../workspace/ 15 | 16 | echo "*** Finished execution of '$0' ***" 17 | -------------------------------------------------------------------------------- /.github/workflows/debpkg.yml: -------------------------------------------------------------------------------- 1 | name: Debian Packaging 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 8 * * *' 8 | 9 | jobs: 10 | build-deb-bookworm: 11 | runs-on: ubuntu-latest 12 | name: Debian pipeline for bookworm 13 | 14 | steps: 15 | - name: Checkout source 16 | uses: actions/checkout@v4 17 | 18 | - name: Execute Docker debpkg action 19 | uses: ./.github/actions/debpkg-bookworm 20 | 21 | - name: Store Debian package artifacts 22 | uses: actions/upload-artifact@v4 23 | with: 24 | name: Debian binary package files for bookworm 25 | path: '*.deb' 26 | 27 | - name: Store Debian package build info 28 | uses: actions/upload-artifact@v4 29 | with: 30 | name: Debian buildinfo file for bookworm 31 | path: '*.buildinfo' 32 | 33 | build-deb-sid: 34 | runs-on: ubuntu-latest 35 | name: Debian pipeline for sid 36 | 37 | steps: 38 | - name: Checkout source 39 | uses: actions/checkout@v4 40 | 41 | - name: Execute Docker debpkg action 42 | uses: ./.github/actions/debpkg-sid 43 | 44 | - name: Store Debian package artifacts 45 | uses: actions/upload-artifact@v4 46 | with: 47 | name: Debian binary package files for sid 48 | path: '*.deb' 49 | 50 | - name: Store Debian package build info 51 | uses: actions/upload-artifact@v4 52 | with: 53 | name: Debian buildinfo file for sid 54 | path: '*.buildinfo' 55 | -------------------------------------------------------------------------------- /.github/workflows/shellcheck.yml: -------------------------------------------------------------------------------- 1 | name: Shellcheck 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 8 * * *' 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout source 15 | uses: actions/checkout@v4 16 | 17 | - name: Display original shellcheck version 18 | run: shellcheck --version 19 | 20 | - name: Update shellcheck to latest stable version 21 | run: | 22 | wget -qO- https://github.com/koalaman/shellcheck/releases/download/stable/shellcheck-stable.linux.x86_64.tar.xz | tar -xJv 23 | sudo cp shellcheck-stable/shellcheck /usr/bin/ 24 | 25 | - name: Display current shellcheck version 26 | run: shellcheck --version 27 | 28 | - name: Shellcheck execution 29 | run: shellcheck --exclude=SC1090,SC1091 debian/*.init debian/*.post* debian/*.pre* debian/*-setup el/*.init tests/simulator-tcp.sh 30 | -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 8 * * *' 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-22.04 12 | name: Run unit tests 13 | 14 | steps: 15 | - name: Checkout source 16 | uses: actions/checkout@v4 17 | 18 | - name: Update APT cache 19 | run: | 20 | sudo apt-get update 21 | 22 | - name: Get build dependencies 23 | run: | 24 | echo "Generating Debian source for usage on Ubuntu/jammy / ubuntu-22.04" 25 | cd pkg/deb/ 26 | ./generator.sh 27 | ./backports/jammy 28 | rm -rf debian 29 | mv jammy debian 30 | echo "Installing Debian packages" 31 | sudo apt-get build-dep -q -y -Ppkg.ngcp-rtpengine.nobcg729 . 32 | 33 | - name: Run unit tests with sanitizers enabled 34 | run: | 35 | make asan-check 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.sw? 2 | *.strhash.c 3 | docs/_* 4 | bencode.c 5 | config.mk 6 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=gerrit.mgm.sipwise.com 3 | port=29418 4 | project=rtpengine 5 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | 2 | Pawel Kuzak 3 | 4 | Stefan Mititelu 5 | Sipwise Jenkins Builder 6 | Balajee SV 7 | Balajee SV 8 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.10" 7 | 8 | sphinx: 9 | configuration: docs/conf.py 10 | 11 | python: 12 | install: 13 | - requirements: docs/requirements.txt 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - gcc 4 | - clang 5 | before_install: 6 | - sudo apt-get update 7 | - sudo apt-get -y install devscripts equivs 8 | # workarounds for Ubuntu xenial/16.04 LTS, 9 | # `apt-get -y build-dep -Ppkg.ngcp-rtpengine.nobcg729 .` 10 | # as well as several Build-Deps aren't available 11 | - sed -i '/libbcg729-dev/d' debian/control 12 | - sed -i 's/default-libmysqlclient-dev/libmysqlclient-dev/' debian/control 13 | - sed -i 's/libiptc-dev/iptables-dev/' debian/control 14 | - sed -i 's/debhelper-compat.*/debhelper,/' debian/control 15 | - mk-build-deps --build-dep --install --remove --root-cmd sudo 16 | script: 17 | - make -C iptables-extension 18 | # `dpkg-parsechangelog [...] -STimestamp` is not available 19 | # on Ubuntu xenial/16.04 LTS 20 | - RELEASE_DATE=travis-$(date +%s) make -C daemon -j$(nproc) 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := all 2 | 3 | with_transcoding ?= yes 4 | 5 | ifeq ($(DO_ASAN_FLAGS),1) 6 | ASAN_FLAGS = -ggdb -O0 -fsanitize=address -fsanitize=leak -fsanitize=undefined 7 | ifeq ($(origin CFLAGS),undefined) 8 | CFLAGS := -Wall -Wextra -Wno-sign-compare -Wno-unused-parameter -Wstrict-prototypes 9 | else 10 | CFLAGS := $(CFLAGS) 11 | endif 12 | CFLAGS += $(ASAN_FLAGS) 13 | CFLAGS += -DASAN_BUILD 14 | LDFLAGS += $(ASAN_FLAGS) 15 | export CFLAGS 16 | export LDFLAGS 17 | export ASAN_OPTIONS=verify_asan_link_order=0 18 | export UBSAN_OPTIONS=print_stacktrace=1 19 | export G_SLICE=always-malloc 20 | endif 21 | 22 | export top_srcdir = $(CURDIR) 23 | 24 | # Initialize all flags, so that we only compute them once. 25 | include lib/deps.Makefile 26 | 27 | include lib/lib.Makefile 28 | 29 | .PHONY: all distclean clean coverity 30 | 31 | all: 32 | $(MAKE) -C daemon 33 | ifeq ($(with_transcoding),yes) 34 | $(MAKE) -C recording-daemon 35 | $(MAKE) -C perf-tester 36 | endif 37 | 38 | install: 39 | $(MAKE) -C daemon install 40 | ifeq ($(with_transcoding),yes) 41 | $(MAKE) -C recording-daemon install 42 | $(MAKE) -C perf-tester install 43 | endif 44 | mkdir -p $(DESTDIR)/usr/libexec/rtpengine/ $(DESTDIR)/usr/bin $(DESTDIR)/usr/share/man/man1 45 | install -m 0755 utils/rtpengine-get-table $(DESTDIR)/usr/libexec/rtpengine/ 46 | install -m 0755 utils/rtpengine-ctl utils/rtpengine-ng-client $(DESTDIR)/usr/bin/ 47 | install -m 0644 utils/rtpengine-ctl.1 utils/rtpengine-ng-client.1 $(DESTDIR)/usr/share/man/man1 48 | 49 | coverity: 50 | $(MAKE) -C daemon 51 | ifeq ($(with_transcoding),yes) 52 | $(MAKE) -C recording-daemon 53 | $(MAKE) -C perf-tester 54 | endif 55 | 56 | .PHONY: with-kernel 57 | 58 | with-kernel: all 59 | $(MAKE) -C kernel-module 60 | 61 | install-with-kernel: all install 62 | $(MAKE) -C kernel-module install 63 | 64 | distclean clean: 65 | $(MAKE) -C daemon clean 66 | $(MAKE) -C recording-daemon clean 67 | $(MAKE) -C perf-tester clean 68 | $(MAKE) -C kernel-module clean 69 | $(MAKE) -C t clean 70 | $(MAKE) -C lib clean 71 | rm -f config.mk 72 | 73 | .DEFAULT: 74 | $(MAKE) -C daemon $@ 75 | $(MAKE) -C recording-daemon $@ 76 | $(MAKE) -C perf-tester 77 | $(MAKE) -C kernel-module $@ 78 | 79 | .PHONY: check asan-check 80 | 81 | check: all 82 | $(MAKE) -C t 83 | 84 | asan-check: 85 | DO_ASAN_FLAGS=1 $(MAKE) check 86 | -------------------------------------------------------------------------------- /daemon/.gitignore: -------------------------------------------------------------------------------- 1 | .depend 2 | *.8 3 | *.o 4 | rtpengine 5 | core 6 | core.* 7 | .ycm_extra_conf.pyc 8 | auxlib.c 9 | loglib.c 10 | rtplib.c 11 | codeclib.c 12 | resample.c 13 | str.c 14 | fix_frame_channel_layout.h 15 | socket.c 16 | streambuf.c 17 | ssllib.c 18 | dtmflib.c 19 | *-test 20 | dtmf_rx_fillin.h 21 | *-test.c 22 | spandsp_logging.h 23 | mvr2s_x64_avx512.S 24 | mvr2s_x64_avx2.S 25 | mix_buffer.c 26 | mix_in_x64_avx2.S 27 | mix_in_x64_avx512bw.S 28 | mix_in_x64_sse2.S 29 | poller.c 30 | bufferpool.c 31 | uring.c 32 | -------------------------------------------------------------------------------- /daemon/arena.c: -------------------------------------------------------------------------------- 1 | #include "arena.h" 2 | 3 | __thread memory_arena_t *memory_arena; 4 | -------------------------------------------------------------------------------- /daemon/load.c: -------------------------------------------------------------------------------- 1 | #include "load.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "helpers.h" 11 | #include "log.h" 12 | #include "main.h" 13 | 14 | int load_average; // times 100 15 | int cpu_usage; // percent times 100 (0 - 9999) 16 | 17 | static long used_last, idle_last; 18 | 19 | enum thread_looper_action load_thread(void) { 20 | // anything to do? 21 | if (!rtpe_config.load_limit && !rtpe_config.cpu_limit) 22 | return TLA_BREAK; 23 | 24 | if (rtpe_config.load_limit) { 25 | double loadavg; 26 | if (getloadavg(&loadavg, 1) >= 1) 27 | g_atomic_int_set(&load_average, (int) (loadavg * 100.0)); 28 | else 29 | ilog(LOG_WARN, "Failed to obtain load average: %s", strerror(errno)); 30 | } 31 | 32 | if (rtpe_config.cpu_limit) { 33 | FILE *f; 34 | f = fopen("/proc/stat", "r"); 35 | if (f) { 36 | long user_now, nice_now, system_now, idle_now; 37 | if (fscanf(f, "cpu %li %li %li %li", 38 | &user_now, &nice_now, &system_now, &idle_now) == 4) 39 | { 40 | long used_now = user_now + nice_now + system_now; 41 | long used_secs = used_now - used_last; 42 | long idle_secs = idle_now - idle_last; 43 | long total_secs = used_secs + idle_secs; 44 | if (total_secs > 0 && used_last && idle_last) 45 | g_atomic_int_set(&cpu_usage, (int) (used_secs 46 | * 10000 / total_secs)); 47 | used_last = used_now; 48 | idle_last = idle_now; 49 | } 50 | else 51 | ilog(LOG_WARN, "Failed to obtain CPU usage"); 52 | fclose(f); 53 | } 54 | } 55 | 56 | return TLA_CONTINUE; 57 | } 58 | -------------------------------------------------------------------------------- /daemon/log.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOG_H__ 2 | #define __LOG_H__ 3 | 4 | 5 | #include "str.h" 6 | #include "loglib.h" 7 | #include "types.h" 8 | 9 | 10 | 11 | struct ice_agent; 12 | enum log_format; 13 | 14 | struct log_info { 15 | union { 16 | call_t *call; 17 | stream_fd *stream_fd; 18 | const str *str; 19 | const char *cstr; 20 | struct ice_agent *ice_agent; 21 | void *ptr; 22 | }; 23 | union { 24 | struct call_media *media; 25 | }; 26 | enum { 27 | LOG_INFO_NONE = 0, 28 | LOG_INFO_CALL, 29 | LOG_INFO_STREAM_FD, 30 | LOG_INFO_STR, 31 | LOG_INFO_C_STRING, 32 | LOG_INFO_ICE_AGENT, 33 | LOG_INFO_MEDIA, 34 | } e; 35 | }; 36 | 37 | extern int _log_facility_cdr; 38 | extern int _log_facility_rtcp; 39 | extern int _log_facility_dtmf; 40 | 41 | 42 | #define LOG_INFO_STACK_SIZE 8 43 | extern __thread struct log_info log_info[LOG_INFO_STACK_SIZE]; 44 | extern __thread unsigned int log_info_idx; 45 | 46 | 47 | 48 | void cdrlog(const char* cdrbuffer); 49 | void rtcplog(const char* cdrbuffer); 50 | void dtmflog(GString *s); 51 | 52 | 53 | void log_format(enum log_format); 54 | void __ilog(int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); 55 | 56 | // call debug 57 | #ifdef __DEBUG 58 | #define __C_DBG(x...) ilog(LOG_DEBUG, x) 59 | #else 60 | #define __C_DBG(x...) ilogs(internals, LOG_DEBUG, x) 61 | #endif 62 | 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /daemon/loglevels.h: -------------------------------------------------------------------------------- 1 | ll(core, "Everything that isn't part of another subsystem") 2 | ll(spandsp, "Log messages generated by SpanDSP directly") 3 | ll(ffmpeg, "Log messages generated by ffmpeg directly") 4 | ll(transcoding, "Media and RTP transcoding") 5 | ll(codec, "Codec negotiation") 6 | ll(rtcp, "RTCP handling") 7 | ll(ice, "ICE negotiation") 8 | ll(crypto, "Negotiation of SRTP, crypto suites, DTLS, SDES") 9 | ll(srtp, "SRTP encryption and decryption") 10 | ll(internals, "Noisy low-level internals") 11 | ll(http, "HTTP, HTTPS, Websockets") 12 | ll(control, "Control protocols including SDP exchanges, CLI") 13 | ll(dtx, "DTX timer/buffer") 14 | -------------------------------------------------------------------------------- /debian/.gitignore: -------------------------------------------------------------------------------- 1 | *.debhelper 2 | *.log 3 | *.substvars 4 | /.debhelper/ 5 | /debhelper-build-stamp 6 | /files 7 | /ngcp-rtpengine-daemon/ 8 | /ngcp-rtpengine-daemon@.service 9 | /ngcp-rtpengine-kernel-dkms/ 10 | /ngcp-rtpengine-kernel-source/ 11 | /ngcp-rtpengine-perftest-data/ 12 | /ngcp-rtpengine-perftest/ 13 | /ngcp-rtpengine-recording-daemon/ 14 | /ngcp-rtpengine-recording-daemon@.service 15 | /ngcp-rtpengine-utils/ 16 | /ngcp-rtpengine/ 17 | /tmp/ 18 | README.html.gz 19 | README.md.gz 20 | -------------------------------------------------------------------------------- /debian/clean: -------------------------------------------------------------------------------- 1 | debian/README.html.gz 2 | debian/README.md.gz 3 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Source: https://www.sipwise.com/ 3 | Upstream-Contact: Sipwise Development Team 4 | 5 | Files: * 6 | Copyright: 7 | Copyright © 2007-2023 Sipwise GmbH, Austria 8 | License: GPL-3+ with OpenSSL exception 9 | This program is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | . 14 | In addition, as a special exception, the copyright holders give 15 | permission to link the code of this release with the OpenSSL project's 16 | "OpenSSL" library (or with modified versions of it that use the same 17 | license as the "OpenSSL" library), and distribute the linked executables. 18 | . 19 | You must obey the GNU General Public License in all respects for all 20 | of the code used other than "OpenSSL". If you modify file(s) with this 21 | exception, you may extend this exception to your version of the file(s), 22 | but you are not obligated to do so. If you do not wish to do so, 23 | delete this exception statement from your version. If you delete 24 | this exception statement from all source files in the program, then 25 | also delete it here. 26 | . 27 | This program is distributed in the hope that it will be useful, 28 | but WITHOUT ANY WARRANTY; without even the implied warranty of 29 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 30 | GNU General Public License for more details. 31 | . 32 | You should have received a copy of the GNU General Public License 33 | along with this program. If not, see . 34 | Comment: 35 | On Debian systems, the full text of the GNU General Public License 36 | version 3 can be found in the file '/usr/share/common-licenses/GPL-3'. 37 | -------------------------------------------------------------------------------- /debian/generate-systemd-templates.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # generate templates 3 | for i in ngcp-rtpengine-daemon ngcp-rtpengine-recording-daemon; do 4 | sed \ 5 | -e 's:daemon\.pid:daemon.%i.pid:g' \ 6 | -e 's:/etc/rtpengine/:/etc/rtpengine_%i/:g' \ 7 | $i.service > $i@.service 8 | done 9 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-daemon.default: -------------------------------------------------------------------------------- 1 | CONFIG_FILE=/etc/rtpengine/rtpengine.conf 2 | # CONFIG_SECTION=rtpengine 3 | PIDFILE=/run/rtpengine/ngcp-rtpengine-daemon.pid 4 | MANAGE_IPTABLES=yes 5 | SET_USER=rtpengine 6 | #SET_GROUP=rtpengine # GROUP only needs to be set if USER is not set or if the user isn't in the group 7 | SET_MASK=0x7 8 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-daemon.dirs: -------------------------------------------------------------------------------- 1 | etc/modprobe.d 2 | var/cache/rtpengine 3 | var/spool/rtpengine 4 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-daemon.install: -------------------------------------------------------------------------------- 1 | etc/rtpengine.conf /etc/rtpengine/ 2 | usr/bin/rtpengine 3 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-daemon.links: -------------------------------------------------------------------------------- 1 | ${env:deb_systemdsystemunitdir}/ngcp-rtpengine-daemon.service ${env:deb_systemdsystemunitdir}/rtpengine-daemon.service 2 | ${env:deb_systemdsystemunitdir}/ngcp-rtpengine-daemon.service ${env:deb_systemdsystemunitdir}/rtpengine.service 3 | ${env:deb_systemdsystemunitdir}/ngcp-rtpengine-daemon@.service ${env:deb_systemdsystemunitdir}/rtpengine-daemon@.service 4 | ${env:deb_systemdsystemunitdir}/ngcp-rtpengine-daemon@.service ${env:deb_systemdsystemunitdir}/rtpengine@.service 5 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-daemon.manpages: -------------------------------------------------------------------------------- 1 | usr/share/man/man8/rtpengine.8 2 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-daemon.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | default=/etc/default/ngcp-rtpengine-daemon 6 | modname=xt_RTPENGINE 7 | 8 | if [ -x "$(which ngcp-virt-identify)" ]; then 9 | if ngcp-virt-identify --type container; then 10 | VIRT="yes" 11 | fi 12 | fi 13 | 14 | if [ "$VIRT" = "yes" ]; then 15 | echo "Container environment detected. Skip kernel module check" 16 | else 17 | if [ -f $default ]; then 18 | . $default || true 19 | fi 20 | 21 | if [ -n "$TABLE" ] && [ "$TABLE" -ge 0 ] && \ 22 | [ -n "$NO_FALLBACK" ] && \ 23 | { [ "$NO_FALLBACK" = "1" ] || [ "$NO_FALLBACK" = "yes" ] ; } 24 | then 25 | if lsmod | grep -q $modname || modinfo $modname > /dev/null 2> /dev/null; then 26 | true 27 | else 28 | echo "Kernel module $modname not found and NO_FALLBACK is set." 29 | echo "Daemon restart not performed." 30 | exit 0 31 | fi 32 | fi 33 | fi 34 | 35 | if [ "$1" = configure ]; then 36 | adduser --system --home /nonexistent --gecos rtpengine \ 37 | --no-create-home --disabled-password \ 38 | --group --quiet rtpengine || true 39 | 40 | rtpdir='/var/spool/rtpengine' 41 | if ! dpkg-statoverride --list "$rtpdir" > /dev/null 2>&1; then 42 | chown rtpengine:rtpengine "$rtpdir" 43 | chmod 0770 "$rtpdir" 44 | fi 45 | 46 | cachedir='/var/cache/rtpengine' 47 | if ! dpkg-statoverride --list "$cachedir" > /dev/null 2>&1; then 48 | chown rtpengine:rtpengine "$cachedir" 49 | chmod 0770 "$cachedir" 50 | fi 51 | 52 | # set up modprobe.d fragment for auto-load usage 53 | if ! [ -f /etc/modprobe.d/rtpengine.conf ] || grep -q DPKG-GENERATED /etc/modprobe.d/rtpengine.conf; then 54 | OPTIONS="options xt_RTPENGINE proc_mask=0x7" 55 | 56 | PUID=$(id -u rtpengine 2> /dev/null) 57 | test -z "$PUID" || OPTIONS="$OPTIONS proc_uid=$PUID" 58 | PGID=$(id -g rtpengine 2> /dev/null) 59 | test -z "$PGID" || OPTIONS="$OPTIONS proc_gid=$PGID" 60 | 61 | ( echo "# DPKG-GENERATED FILE"; 62 | echo "$OPTIONS" ) > /etc/modprobe.d/rtpengine.conf 63 | fi 64 | fi 65 | 66 | #DEBHELPER# 67 | 68 | exit 0 69 | 70 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-daemon.postrm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ "$1" = purge ]; then 6 | deluser --quiet --system rtpengine > /dev/null || true 7 | delgroup --quiet --system rtpengine > /dev/null || true 8 | 9 | if [ -f /etc/modprobe.d/rtpengine.conf ] && grep -q DPKG-GENERATED /etc/modprobe.d/rtpengine.conf; then 10 | rm -f /etc/modprobe.d/rtpengine.conf 11 | fi 12 | fi 13 | 14 | #DEBHELPER# 15 | 16 | exit 0 17 | 18 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-dev.install: -------------------------------------------------------------------------------- 1 | daemon/*.h /usr/include/rtpengine/ 2 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-kernel-dkms.dkms: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME="ngcp-rtpengine" 2 | PACKAGE_VERSION="#MODULE_VERSION#" 3 | 4 | BUILD_EXCLUSIVE_KERNEL_MIN="4.4" 5 | 6 | MAKE[0]="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build RTPENGINE_VERSION=\"${PACKAGE_VERSION}\"" 7 | CLEAN="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean" 8 | AUTOINSTALL=yes 9 | BUILT_MODULE_NAME[0]="xt_RTPENGINE" 10 | DEST_MODULE_LOCATION[0]=/extra 11 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-kernel-dkms.install: -------------------------------------------------------------------------------- 1 | kernel-module/*.c usr/src/${env:DEB_SOURCE}-${env:DEB_VERSION_UPSTREAM} 2 | kernel-module/*.h usr/src/${env:DEB_SOURCE}-${env:DEB_VERSION_UPSTREAM} 3 | kernel-module/*.inc usr/src/${env:DEB_SOURCE}-${env:DEB_VERSION_UPSTREAM} 4 | kernel-module/Makefile usr/src/${env:DEB_SOURCE}-${env:DEB_VERSION_UPSTREAM} 5 | kernel-module/gen-* usr/src/${env:DEB_SOURCE}-${env:DEB_VERSION_UPSTREAM} 6 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-kernel-dkms.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ -x "$(which ngcp-virt-identify)" ]; then 6 | if ngcp-virt-identify --type container; then 7 | VIRT="yes" 8 | fi 9 | fi 10 | 11 | #DEBHELPER# 12 | 13 | if [ "$VIRT" = "yes" ]; then 14 | echo "Container environment detected. Skip daemon" 15 | else 16 | if [ "$1" = 'configure' ] ; then 17 | # try to start the daemon 18 | if [ -x /etc/init.d/ngcp-rtpengine-daemon ] ; then 19 | invoke-rc.d ngcp-rtpengine-daemon restart || true 20 | fi 21 | fi 22 | fi 23 | 24 | exit 0 25 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-kernel-dkms.prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ -x "$(command -v ngcp-virt-identify)" ]; then 6 | if ngcp-virt-identify --type container; then 7 | VIRT="yes" 8 | fi 9 | fi 10 | 11 | if [ "$VIRT" = "yes" ]; then 12 | echo "Container environment detected. Skip daemon" 13 | else 14 | # make sure it's not running 15 | if [ -x /etc/init.d/ngcp-rtpengine-daemon ] ; then 16 | invoke-rc.d ngcp-rtpengine-daemon stop || true 17 | rmmod "xt_RTPENGINE" 2>/dev/null || true 18 | fi 19 | fi 20 | 21 | #DEBHELPER# 22 | 23 | exit 0 24 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-perftest-data.install: -------------------------------------------------------------------------------- 1 | fixtures/* /usr/share/rtpengine-perftest/ 2 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-perftest.install: -------------------------------------------------------------------------------- 1 | usr/bin/rtpengine-perftest 2 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-recording-daemon.default: -------------------------------------------------------------------------------- 1 | CONFIG_FILE=/etc/rtpengine/rtpengine-recording.conf 2 | # CONFIG_SECTION=rtpengine-recording 3 | PIDFILE=/run/rtpengine-recording/ngcp-rtpengine-recording-daemon.pid 4 | #SET_USER=root 5 | #SET_GROUP=root # GROUP only needs to be set if USER is not set or if the user isn't in the group 6 | # 7 | MUST_NFS=no 8 | NFS_HOST=192.168.1.1 9 | NFS_REMOTE_PATH=/var/recordings 10 | NFS_LOCAL_MOUNT=/var/lib/rtpengine-recording # must match output-dir if used 11 | NFS_OPTIONS=hard,intr,tcp 12 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-recording-daemon.dirs: -------------------------------------------------------------------------------- 1 | var/lib/rtpengine-recording 2 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-recording-daemon.install: -------------------------------------------------------------------------------- 1 | debian/ngcp-rtpengine-recording-nfs-setup /usr/sbin/ 2 | etc/rtpengine-recording.conf /etc/rtpengine/ 3 | usr/bin/rtpengine-recording 4 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-recording-daemon.links: -------------------------------------------------------------------------------- 1 | ${env:deb_systemdsystemunitdir}/ngcp-rtpengine-recording-daemon.service ${env:deb_systemdsystemunitdir}/rtpengine-recording-daemon.service 2 | ${env:deb_systemdsystemunitdir}/ngcp-rtpengine-recording-daemon.service ${env:deb_systemdsystemunitdir}/rtpengine-recording.service 3 | ${env:deb_systemdsystemunitdir}/ngcp-rtpengine-recording-daemon@.service ${env:deb_systemdsystemunitdir}/rtpengine-recording-daemon@.service 4 | ${env:deb_systemdsystemunitdir}/ngcp-rtpengine-recording-daemon@.service ${env:deb_systemdsystemunitdir}/rtpengine-recording@.service 5 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-recording-daemon.manpages: -------------------------------------------------------------------------------- 1 | debian/ngcp-rtpengine-recording-nfs-setup.1 2 | usr/share/man/man8/rtpengine-recording.8 3 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-recording-daemon.ngcp-rtpengine-recording-nfs-mount.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=NGCP RTP/media Recording Daemon NFS mount point 3 | After=network-online.target 4 | After=remote-fs.target 5 | After=rpcbind.socket 6 | After=rpcbind.service 7 | After=nfs-client.target 8 | Requires=network-online.target 9 | Requires=rpcbind.socket 10 | Requires=rpcbind.service 11 | Requires=nfs-client.target 12 | 13 | [Service] 14 | Type=simple 15 | RemainAfterExit=yes 16 | ExecStart=/usr/sbin/ngcp-rtpengine-recording-nfs-setup start 17 | ExecStop=/usr/sbin/ngcp-rtpengine-recording-nfs-setup stop 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-recording-daemon.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ "$1" = configure ]; then 6 | rtpdir='/var/lib/rtpengine-recording' 7 | if ! dpkg-statoverride --list "$rtpdir" > /dev/null 2>&1; then 8 | chown rtpengine:rtpengine "$rtpdir" 9 | chmod 0770 "$rtpdir" 10 | fi 11 | fi 12 | 13 | #DEBHELPER# 14 | 15 | exit 0 16 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-recording-daemon.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=NGCP RTP/media Recording Daemon 3 | After=network-online.target 4 | After=remote-fs.target 5 | After=ngcp-rtpengine-recording-nfs-mount.service 6 | Requires=network-online.target 7 | Requires=ngcp-rtpengine-recording-nfs-mount.service 8 | 9 | [Service] 10 | Type=notify 11 | LimitNOFILE=100000 12 | RuntimeDirectory=rtpengine-recording 13 | PIDFile=/run/rtpengine-recording/ngcp-rtpengine-recording-daemon.pid 14 | AmbientCapabilities=CAP_NET_ADMIN CAP_CHOWN 15 | CapabilityBoundingSet=CAP_NET_ADMIN CAP_CHOWN 16 | User=rtpengine 17 | Group=rtpengine 18 | ExecStart=/usr/bin/rtpengine-recording -f -E --no-log-timestamps --pidfile /run/rtpengine-recording/ngcp-rtpengine-recording-daemon.pid --config-file /etc/rtpengine/rtpengine-recording.conf 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-recording-nfs-setup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | PATH=/sbin:/bin:/usr/sbin:/usr/bin 6 | DEFAULTS=/etc/default/ngcp-rtpengine-recording-daemon 7 | DESC="RTP engine recording NFS share" 8 | 9 | . /lib/lsb/init-functions 10 | 11 | # Load startup options if available 12 | if [ -f "$DEFAULTS" ]; then 13 | . "$DEFAULTS" || true 14 | fi 15 | 16 | [ -z "$NFS_OPTIONS" ] && NFS_OPTIONS="hard,tcp,intr" 17 | 18 | ### 19 | 20 | case "$1" in 21 | start) 22 | if [ "$MUST_NFS" = yes ]; then 23 | if ! grep -E -q "^[^ :]+:[^ :]+ $NFS_LOCAL_MOUNT nfs.? " /proc/mounts; then 24 | log_action_msg "Mounting $DESC" 25 | test -d "$NFS_LOCAL_MOUNT" || mkdir -p "$NFS_LOCAL_MOUNT" 26 | mount -t nfs -o "$NFS_OPTIONS" "$NFS_HOST:$NFS_REMOTE_PATH" "$NFS_LOCAL_MOUNT" 27 | fi 28 | fi 29 | ;; 30 | stop) 31 | if grep -E -q "^[^ :]+:[^ :]+ $NFS_LOCAL_MOUNT nfs.? " /proc/mounts; then 32 | log_action_msg "Unmounting $DESC" 33 | umount "$NFS_LOCAL_MOUNT" 34 | fi 35 | ;; 36 | *) 37 | echo "Usage: $0 {start|stop}" >&2 38 | exit 1 39 | ;; 40 | esac 41 | 42 | exit 0 43 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-recording-nfs-setup.1: -------------------------------------------------------------------------------- 1 | .TH man 1 "17 May 2022" "1.0" "ngcp-rtpengine-recording-nfs-setup man page" 2 | .SH NAME 3 | ngcp\-rtpengine\-recording\-nfs\-setup \- manages NFS mount for ngcp\-rtpengine\-recording\-daemon service 4 | .SH SYNOPSIS 5 | ngcp\-rtpengine\-recording\-nfs\-setup [start|stop] 6 | .SH DESCRIPTION 7 | ngcp\-rtpengine\-recording\-nfs\-setup will mount or unmount the NFS resource needed by ngcp\-rtpengine\-recording\-daemon to properly work. 8 | This program will be called by ngcp\-rtpengine\-recording\-daemon service. 9 | .SH OPTIONS 10 | ngcp\-rtpengine\-recording\-nfs\-setup has no options. 11 | .SH SEE ALSO 12 | rtpengine\-recording(8) 13 | .SH BUGS 14 | No known bugs. 15 | .SH AUTHOR 16 | Victor Seva (vseva@sipwise.com) 17 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-utils.install: -------------------------------------------------------------------------------- 1 | perl/* /usr/share/perl5/ 2 | usr/bin/rtpengine-ctl 3 | usr/bin/rtpengine-ng-client 4 | usr/libexec/rtpengine/rtpengine-get-table 5 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine-utils.manpages: -------------------------------------------------------------------------------- 1 | usr/share/man/man1/rtpengine-ctl.1 2 | usr/share/man/man1/rtpengine-ng-client.1 3 | -------------------------------------------------------------------------------- /debian/ngcp-rtpengine.docs: -------------------------------------------------------------------------------- 1 | debian/README.html.gz 2 | debian/README.md.gz 3 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | # Uncomment this to turn on verbose mode. 5 | # export DH_VERBOSE=1 6 | 7 | include /usr/share/dpkg/pkg-info.mk 8 | 9 | export DEB_VERSION_UPSTREAM 10 | export DEB_SOURCE 11 | 12 | ifneq (,$(filter $(DEB_BUILD_PROFILES),pkg.ngcp-rtpengine.no-transcoding)) 13 | export with_transcoding = no 14 | endif 15 | ifneq (,$(filter $(DEB_BUILD_PROFILES),pkg.ngcp-rtpengine.asan)) 16 | export DO_ASAN_FLAGS = 1 17 | # this prevents dh from setting default (including -O2) which we don't want 18 | export DEB_CFLAGS_MAINT_SET = 19 | export DEB_LDFLAGS_MAINT_SET = 20 | endif 21 | 22 | export FIXTURES_PATH = /usr/share/rtpengine-perftest 23 | 24 | export deb_systemdsystemunitdir := $(shell pkg-config --variable=systemdsystemunitdir systemd) 25 | 26 | %: 27 | dh $@ 28 | 29 | execute_before_dh_auto_configure: 30 | (cd debian && sh generate-systemd-templates.sh) 31 | 32 | execute_before_dh_auto_install-indep: 33 | # markdown README 34 | markdown README.md | gzip -9 > debian/README.html.gz 35 | gzip -9 < README.md > debian/README.md.gz 36 | 37 | execute_after_dh_installsystemd: 38 | dh_installsystemd -pngcp-rtpengine-recording-daemon --name=ngcp-rtpengine-recording-nfs-mount 39 | 40 | override_dh_dkms: 41 | dh_dkms -p$(DEB_SOURCE)-kernel-dkms -V $(DEB_VERSION_UPSTREAM) 42 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/source/lintian-overrides: -------------------------------------------------------------------------------- 1 | ngcp-rtpengine source: missing-build-dependency-for-dh_-command dh_dkms => dkms 2 | -------------------------------------------------------------------------------- /debian/source/options: -------------------------------------------------------------------------------- 1 | extend-diff-ignore=.gitreview 2 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS = 7 | SPHINXBUILD = sphinx-build 8 | # SOURCEDIR = source 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/call_recording.md: -------------------------------------------------------------------------------- 1 | # Call Recording 2 | 3 | Call recording can be accomplished in one of two ways: 4 | 5 | * The *rtpengine* daemon can write `libpcap`-formatted captures directly (`--recording-method=pcap`); 6 | 7 | * The *rtpengine* daemon can write audio frames into a sink in `/proc/rtpengine` (`--recording-method=proc`). These frames must then be consumed within a short period by another process; while this can be any process, the packaged `rtpengine-recording` daemon is a useful ready implementation of a call recording solution. The recording daemon uses `ffmpeg` libraries to implement a variety of on-the-fly format conversion and mixing options, as well as metadata logging. See `rtpengine-recording -h` for details. 8 | 9 | **Important note**: The *rtpengine* daemon emits data into a "spool directory" (`--recording-dir` option), by default `/var/spool/rtpengine`. The recording daemon is then configured to consume this using the `--spool-dir` option, and to store the final emitted recordings (in whatever desired target format, etc.) in `--output-dir`. Ensure that the `--spool-dir` and the `--output-dir` are **different** directories, or you will run into problems (as discussed in [#81](https://github.com/sipwise/rtpengine/issues/808)). 10 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # For the full list of built-in configuration values, see the documentation: 4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 5 | 6 | # -- Project information ----------------------------------------------------- 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 8 | 9 | from datetime import date 10 | 11 | project = 'rtpengine' 12 | copyright = str(date.today().year) + ', Sipwise' 13 | author = 'Sipwise' 14 | release = 'master' 15 | 16 | # -- General configuration --------------------------------------------------- 17 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 18 | 19 | extensions = ["myst_parser", 20 | "sphinx_rtd_theme"] 21 | myst_heading_anchors = 3 22 | 23 | templates_path = ['_templates'] 24 | exclude_patterns = ['_build'] 25 | 26 | master_doc = 'index' 27 | 28 | # -- Options for HTML output ------------------------------------------------- 29 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 30 | 31 | html_theme = 'sphinx_rtd_theme' 32 | htmlhelp_basename = 'rtpenginedoc' 33 | -------------------------------------------------------------------------------- /docs/glossary.md: -------------------------------------------------------------------------------- 1 | Glossary 2 | ======== 3 | 4 | This is the page, which gives a clue regarding abbreviations and specific words used in the rtpengine project. 5 | 6 | --- 7 | 8 | * tag - is a monologue related tag (value of which is taken from either From or To tag) 9 | * monologue - is a `call_monologue` = a half of dialogue -------------------------------------------------------------------------------- /docs/images/pic1_rtp_engine_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/docs/images/pic1_rtp_engine_architecture.png -------------------------------------------------------------------------------- /docs/images/pic2_rtp_engine_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/docs/images/pic2_rtp_engine_architecture.png -------------------------------------------------------------------------------- /docs/images/pic3_media_socket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/docs/images/pic3_media_socket.png -------------------------------------------------------------------------------- /docs/images/pic4_flags_parsing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/docs/images/pic4_flags_parsing.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. _rtpengine: https://github.com/sipwise/rtpengine 2 | .. _Sipwise: https://www.sipwise.com 3 | .. _Kamailio: https://www.kamailio.org/ 4 | 5 | Welcome to the Rtpengine Project Documentation 6 | ============================================== 7 | 8 | .. toctree:: 9 | :maxdepth: 4 10 | 11 | Overview 12 | 13 | ============================================== 14 | 15 | .. toctree:: 16 | :maxdepth: 1 17 | 18 | usage 19 | Option flags (NG control protocol) 20 | 21 | ============================================== 22 | 23 | .. toctree:: 24 | :maxdepth: 1 25 | 26 | Daemon manual page: start and configuration options 27 | Recording daemon manual page: start and configuration options 28 | 29 | ============================================== 30 | 31 | .. toctree:: 32 | :maxdepth: 2 33 | 34 | architecture 35 | tests 36 | troubleshooting 37 | transcoding 38 | glossary 39 | -------------------------------------------------------------------------------- /docs/janus_interface_and_replacement.md: -------------------------------------------------------------------------------- 1 | # *Janus* Interface and Replacement Functionality 2 | 3 | *Rtpengine* supports a limited and narrow subset of the features provided by 4 | [Janus](https://janus.conf.meetecho.com/), specifically the basic business 5 | logic behind the *videoroom* plugin. This makes it possible to use *rtpengine* 6 | as a drop-in replacement for *Janus* for this one specific use case, and has 7 | the benefit of being able to use all the extra features that *rtpengine* 8 | provides, such as transcoding, in-kernel packet forwarding for improved 9 | performance, etc. 10 | 11 | The required subset of the *Janus* API is exposed via *rtpengine*'s HTTP/WS 12 | interface. The HTTP admin API is connected to the `/admin` URI path using a 13 | JSON payload (same as *Janus* does), while the module communication happens on 14 | the WS protocol `janus-protocol`, also with JSON payloads (same as *Janus* 15 | does). Unlike *Janus*, both HTTP and WS endpoints are running on the same port. 16 | In fact, there is no real distinction between both interfaces, therefore both 17 | admin and non-admin messages can be sent via either interface. HTTPS and WSS 18 | are also supported. 19 | 20 | Token-based plugin authentication works similar to how it works in *Janus* 21 | except that only the single *videoroom* plugin is supported. The configuration 22 | setting `janus-secret` must be set to enable clients to connect to this 23 | simulated *Janus* interface and make use of its features. 24 | 25 | Under the hood the functionality of the *videoroom* plugin is facilitated using 26 | *rtpengine*'s `publish` and `subscribe` methods, which are mapped directly to 27 | the respective *Janus* methods. One *Janus* video room becomes one *rtpengine* 28 | call, with a distinctive and unique call ID based on the video room ID. 29 | 30 | There's currently no support for customising the SDP features and options used 31 | within the *Janus* drop-in mode, and, as *Janus* is WebRTC-specific, all SDPs 32 | produced from this mode can be used directly by WebRTC clients. Non-WebRTC 33 | clients can participate in the same video room as *Janus* clients if the 34 | respective mapped `publish` and `subscribe` methods are used, and with the call 35 | ID mapped to the video room ID. 36 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/overview.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | myst-parser==2.0.0 2 | sphinx-rtd-theme>=1.2 -------------------------------------------------------------------------------- /docs/tcpng_control_protocol.md: -------------------------------------------------------------------------------- 1 | # The *tcp-ng* Control Protocol 2 | 3 | *rtpengine* also has support for *ng* control protocol where transport is 4 | TCP (If enabled in the config via the --listen-tcp-ng option). Everything 5 | said for UDP based *ng* protocol counts for TCP variant too. 6 | -------------------------------------------------------------------------------- /el/build-with-mock.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # Sample build script to package RPM using mock 3 | # Usage: el/build-with-mock.sh 4 | # 5 | # el/build-with-mock.sh 12.1.0.0+0~mr12.1.0.0 master 6 | 7 | 8 | set -e 9 | 10 | if [[ -z $1 || -z $2 ]]; then 11 | echo $0: Require package version and git commit 12 | echo "Usage: build-with-mock.sh " 13 | exit 1 14 | fi 15 | 16 | RTPENGINE_VERSION=$1 17 | GIT_COMMIT=$2 18 | 19 | mkdir -p rpmbuild/{SOURCES,SPECS} 20 | 21 | git archive --format=tar --prefix=ngcp-rtpengine-${RTPENGINE_VERSION}/ $2 | gzip -c > rpmbuild/SOURCES/ngcp-rtpengine-${RTPENGINE_VERSION}.tar.gz 22 | 23 | 24 | sed /^Version/s"/^Version:.*/Version: ${RTPENGINE_VERSION}/" el/rtpengine.spec > rpmbuild/SPECS/rtpengine.spec 25 | 26 | rm -f rpmbuild/SRPMS/*.src.rpm 27 | rpmbuild --define "_topdir $PWD/rpmbuild" -bs rpmbuild/SPECS/rtpengine.spec 28 | 29 | echo ======================================= 30 | echo "You may now build for EL8/EL9" 31 | 32 | 33 | echo "EL8: mock -r el/rtpengine-8-x86_64.cfg $(ls rpmbuild/SRPMS/*.src.rpm)" 34 | echo "EL9: mock -r el/rtpengine-9-x86_64.cfg $(ls rpmbuild/SRPMS/*.src.rpm)" 35 | echo ======================================= 36 | -------------------------------------------------------------------------------- /el/rtpengine-8-x86_64.cfg: -------------------------------------------------------------------------------- 1 | include('templates/almalinux-8.tpl') 2 | include('templates/epel-8.tpl') 3 | 4 | config_opts['dnf.conf'] += """ 5 | 6 | [rpmfusion-free-updates] 7 | name=RPM Fusion for EL $releasever - Free - Updates 8 | #baseurl=https://download1.rpmfusion.org/free/el/updates/$releasever/$basearch/ 9 | metalink=https://mirrors.rpmfusion.org/metalink?repo=free-el-updates-released-$releasever&arch=$basearch 10 | enabled=1 11 | 12 | [rpmfusion-nonfree-updates] 13 | name=RPM Fusion for EL $releasever - Nonfree - Updates 14 | #baseurl=https://download1.rpmfusion.org/nonfree/el/updates/$releasever/$basearch/ 15 | metalink=https://mirrors.rpmfusion.org/metalink?repo=nonfree-el-updates-released-$releasever&arch=$basearch 16 | enabled=1 17 | 18 | [copr:copr.fedorainfracloud.org:beaveryoga:broadvoice] 19 | name=Copr repo for broadvoice owned by beaveryoga 20 | baseurl=https://download.copr.fedorainfracloud.org/results/beaveryoga/broadvoice/epel-$releasever-$basearch/ 21 | skip_if_unavailable=True 22 | gpgcheck=1 23 | gpgkey=https://download.copr.fedorainfracloud.org/results/beaveryoga/broadvoice/pubkey.gpg 24 | repo_gpgcheck=0 25 | enabled=1 26 | enabled_metadata=1 27 | """ 28 | 29 | config_opts['chroot_additional_packages'] = "perl-interpreter libdb-devel gdbm-devel libuuid-devel speexdsp-devel" 30 | config_opts['chroot_additional_packages'] += " spandsp3-devel perl-podlators pandoc libatomic" 31 | 32 | config_opts['root'] = 'rtpengine-8-x86_64' 33 | config_opts['target_arch'] = 'x86_64' 34 | config_opts['legal_host_arches'] = ('x86_64',) 35 | 36 | -------------------------------------------------------------------------------- /el/rtpengine-9-x86_64.cfg: -------------------------------------------------------------------------------- 1 | include('templates/almalinux-9.tpl') 2 | include('templates/epel-9.tpl') 3 | 4 | config_opts['dnf.conf'] += """ 5 | 6 | [rpmfusion-free-updates] 7 | name=RPM Fusion for EL $releasever - Free - Updates 8 | #baseurl=https://download1.rpmfusion.org/free/el/updates/$releasever/$basearch/ 9 | metalink=https://mirrors.rpmfusion.org/metalink?repo=free-el-updates-released-$releasever&arch=$basearch 10 | enabled=1 11 | 12 | [rpmfusion-nonfree-updates] 13 | name=RPM Fusion for EL $releasever - Nonfree - Updates 14 | baseurl=https://download1.rpmfusion.org/nonfree/el/updates/$releasever/$basearch/ 15 | #metalink=https://mirrors.rpmfusion.org/metalink?repo=nonfree-el-updates-released-$releasever&arch=$basearch 16 | enabled=1 17 | 18 | [copr:copr.fedorainfracloud.org:beaveryoga:broadvoice] 19 | name=Copr repo for broadvoice owned by beaveryoga 20 | baseurl=https://download.copr.fedorainfracloud.org/results/beaveryoga/broadvoice/epel-$releasever-$basearch/ 21 | skip_if_unavailable=True 22 | gpgcheck=1 23 | gpgkey=https://download.copr.fedorainfracloud.org/results/beaveryoga/broadvoice/pubkey.gpg 24 | repo_gpgcheck=0 25 | enabled=1 26 | enabled_metadata=1 27 | """ 28 | 29 | config_opts['chroot_additional_packages'] = "perl-interpreter libdb-devel gdbm-devel libuuid-devel speexdsp-devel" 30 | config_opts['chroot_additional_packages'] += " spandsp3-devel perl-podlators pandoc" 31 | config_opts['chroot_additional_packages'] += " gcc make autoconf automake gcc-c++ libtool libatomic" 32 | 33 | config_opts['root'] = 'rtpengine-9-x86_64' 34 | config_opts['target_arch'] = 'x86_64' 35 | config_opts['legal_host_arches'] = ('x86_64',) 36 | 37 | -------------------------------------------------------------------------------- /el/rtpengine-recording.init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # rtpengine-recording Startup script for NGCP rtpengine-recording 4 | # 5 | # chkconfig: 345 84 16 6 | # description: NGCP rtpengine-recording 7 | # 8 | # processname: rtpengine-recording 9 | # config: /etc/sysconfig/rtpengine-recording 10 | # pidfile: /run/rtpengine-recording.pid 11 | # 12 | ### BEGIN INIT INFO 13 | # Provides: rtpengine 14 | # Required-Start: $local_fs $network 15 | # Short-Description: NGCP rtpengine-recording 16 | # Description: NGCP rtpengine-recording 17 | ### END INIT INFO 18 | 19 | # Source function library. 20 | . /etc/rc.d/init.d/functions 21 | 22 | if [ -f /etc/sysconfig/rtpengine-recording ] 23 | then 24 | . /etc/sysconfig/rtpengine-recording 25 | else 26 | echo "Error: /etc/sysconfig/rtpengine-recording not present" >&2 27 | exit 6 28 | fi 29 | 30 | prog=rtpengine-recording 31 | runfile=/usr/bin/${prog} 32 | pidfile=${PIDFILE-/run/rtpengine-recording.pid} 33 | lockfile=${LOCKFILE-/var/lock/subsys/rtpengine-recording} 34 | 35 | RETVAL=0 36 | OPTS="" 37 | 38 | [ -z "$CONFIG_FILE" ] || OPTS+=" --config-file=$CONFIG_FILE" 39 | [ -z "$PIDFILE" ] || OPTS+=" --pidfile=$PIDFILE" 40 | 41 | start() { 42 | echo -n $"Starting $prog: " 43 | if [[ -n "$RE_USER" ]] 44 | then 45 | # shellcheck disable=SC2086 46 | daemon --user "$RE_USER" --pidfile="${pidfile}" "$runfile" $OPTS 47 | else 48 | # shellcheck disable=SC2086 49 | daemon --pidfile="${pidfile}" "$runfile" $OPTS 50 | fi 51 | RETVAL=$? 52 | echo 53 | [ $RETVAL = 0 ] && touch "${lockfile}" 54 | return $RETVAL 55 | } 56 | 57 | stop() { 58 | echo -n $"Stopping $prog: " 59 | killproc -p "${pidfile}" "$runfile" 60 | RETVAL=$? 61 | [ $RETVAL = 0 ] && rm -f "${lockfile}" "${pidfile}" 62 | } 63 | 64 | # See how we were called. 65 | case "$1" in 66 | start) 67 | start 68 | ;; 69 | stop) 70 | stop 71 | ;; 72 | status) 73 | status -p "${pidfile}" "$runfile" 74 | RETVAL=$? 75 | ;; 76 | restart) 77 | stop 78 | start 79 | ;; 80 | condrestart|try-restart) 81 | if status -p "${pidfile}" "$runfile" >&/dev/null; then 82 | stop 83 | start 84 | fi 85 | ;; 86 | *) 87 | echo $"Usage: $prog {start|stop|restart|condrestart|try-restart|status}" 88 | RETVAL=2 89 | esac 90 | 91 | exit $RETVAL 92 | -------------------------------------------------------------------------------- /el/rtpengine-recording.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=NGCP RTP/media Recording Daemon 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | Type=forking 8 | LimitNOFILE=100000 9 | Environment=CFG_FILE=/etc/rtpengine/rtpengine-recording.conf 10 | EnvironmentFile=/etc/sysconfig/rtpengine-recording 11 | User=ngcp-rtpengine 12 | Group=ngcp-rtpengine 13 | AmbientCapabilities=CAP_NET_ADMIN CAP_CHOWN 14 | CapabilityBoundingSet=CAP_NET_ADMIN CAP_CHOWN 15 | RuntimeDirectory=rtpengine-recording 16 | PIDFile=/run/rtpengine-recording/rtpengine-recording.pid 17 | ExecStart=/usr/bin/rtpengine-recording --config-file=${CFG_FILE} --pidfile=${PID_FILE} 18 | TimeoutSec=15s 19 | Restart=on-failure 20 | 21 | [Install] 22 | WantedBy=multi-user.target 23 | Alias=ngcp-rtpengine-recording.service 24 | -------------------------------------------------------------------------------- /el/rtpengine-recording.sysconfig: -------------------------------------------------------------------------------- 1 | # For more information on configuring rtpengine see 2 | # http://github.com/sipwise/rtpengine 3 | # 4 | CONFIG_FILE=/etc/rtpengine/rtpengine-recording.conf 5 | PID_FILE=/run/rtpengine-recording/rtpengine-recording.pid -------------------------------------------------------------------------------- /el/rtpengine.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=NGCP RTP/media Proxy Daemon 3 | After=network-online.target 4 | 5 | [Service] 6 | Type=notify 7 | Environment=CFG_FILE=/etc/rtpengine/rtpengine.conf 8 | EnvironmentFile=/etc/sysconfig/rtpengine 9 | User=ngcp-rtpengine 10 | Group=ngcp-rtpengine 11 | AmbientCapabilities=CAP_NET_ADMIN CAP_SYS_NICE 12 | CapabilityBoundingSet=CAP_NET_ADMIN CAP_SYS_NICE 13 | LimitNOFILE=150000 14 | RuntimeDirectory=rtpengine 15 | PIDFile=/run/rtpengine/rtpengine.pid 16 | ExecStart=/usr/bin/rtpengine --config-file=${CFG_FILE} --pidfile=${PID_FILE} -f 17 | RestartSec=3s 18 | TimeoutSec=15s 19 | Restart=on-failure 20 | 21 | [Install] 22 | WantedBy=multi-user.target 23 | Alias=ngcp-rtpengine.service 24 | -------------------------------------------------------------------------------- /el/rtpengine.sysconfig: -------------------------------------------------------------------------------- 1 | # For more information on configuring rtpengine see 2 | # http://github.com/sipwise/rtpengine 3 | # main config file 4 | CONFIG_FILE=/etc/rtpengine/rtpengine.conf 5 | # pid 6 | PID_FILE=/run/rtpengine/rtpengine.pid 7 | # user and group for /proc interface 8 | SET_USER=ngcp-rtpengine 9 | SET_GROUP=ngcp-rtpengine -------------------------------------------------------------------------------- /etc/rtpengine-recording.conf: -------------------------------------------------------------------------------- 1 | [rtpengine-recording] 2 | 3 | table = 0 4 | 5 | ### number of worker threads (default 8) 6 | # num-threads = 16 7 | 8 | ### where to forward to (unix socket) 9 | # forward-to = /run/rtpengine/sock 10 | 11 | ### where to store recordings: file (default), db, both 12 | # output-storage = db 13 | 14 | ### format of stored recordings: wav (default), mp3 15 | # output-format = mp3 16 | 17 | ### directory containing rtpengine metadata files 18 | # spool-dir = /var/spool/rtpengine 19 | 20 | ### where to store media files to 21 | # output-dir = /var/lib/rtpengine-recording 22 | 23 | ### file name pattern for output files 24 | # output-pattern = %c-%t 25 | 26 | ### resample all output audio 27 | # resample-to = 8000 28 | 29 | ### bits per second for MP3 encoding 30 | # mp3-bitrate = 24000 31 | 32 | ### mix participating sources into a single output 33 | # output-mixed = true 34 | 35 | ### maximum number of inputs for mixed output 36 | # mix-num-inputs = 4 37 | 38 | ### create one output file for each source 39 | # output-single = true 40 | 41 | ### flush output to disk after each packet 42 | # flush-packets = true 43 | 44 | ### TCP/TLS output of PCM audio 45 | # tcp-send-to = 10.4.1.7:15413 46 | # tcp-resample = 16000 47 | ### OR 48 | # tls-send-to = 10.4.1.7:15413 49 | # tls-resample = 16000 50 | 51 | ### mysql configuration for db storage 52 | # mysql-host = localhost 53 | # mysql-port = 3306 54 | # mysql-user = rtpengine 55 | # mysql-pass = secret 56 | # mysql-db = rtpengine 57 | 58 | ### ownership/permission control for output files 59 | # output-chmod = 0640 60 | # output-chmod-dir = 0750 61 | # output-chown = rtpengine 62 | # output-chgrp = rtpengine 63 | 64 | ### HTTP notifications for finished recordings 65 | # notify-uri = https://example.com/rec/finished 66 | # notify-post = false 67 | # notify-no-verify = false 68 | # notify-purge = false 69 | # notify-concurrency = 5 70 | # notify-retries = 10 71 | -------------------------------------------------------------------------------- /fixtures/LICENSE: -------------------------------------------------------------------------------- 1 | "Kaer Trouz - Falls Through Walls (80s edit)" 2 | by dotjot 3 | 4 | 2024 - Licensed under 5 | Creative Commons 6 | Attribution (3.0) 7 | 8 | https://creativecommons.org/licenses/by/3.0/ 9 | 10 | https://ccmixter.org/files/dotjot/67669 11 | -------------------------------------------------------------------------------- /fixtures/opus.1.8k.11k.music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/fixtures/opus.1.8k.11k.music.ogg -------------------------------------------------------------------------------- /fixtures/opus.1.8k.11k.speech.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/fixtures/opus.1.8k.11k.speech.ogg -------------------------------------------------------------------------------- /fixtures/opus.2.48k.96k.music.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/fixtures/opus.2.48k.96k.music.ogg -------------------------------------------------------------------------------- /fixtures/opus.2.48k.96k.speech.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/fixtures/opus.2.48k.96k.speech.ogg -------------------------------------------------------------------------------- /fixtures/pcma.1.8k.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/fixtures/pcma.1.8k.raw -------------------------------------------------------------------------------- /fixtures/pcmu.1.8k.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/fixtures/pcmu.1.8k.raw -------------------------------------------------------------------------------- /include/audio_player.h: -------------------------------------------------------------------------------- 1 | #ifndef _AUDIO_PLAYER_H_ 2 | #define _AUDIO_PLAYER_H_ 3 | 4 | #ifdef WITH_TRANSCODING 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "types.h" 11 | 12 | /* 13 | * Similar to the existing media_player, but instead of simply producing 14 | * its own standalone output media stream, the audio_player takes over the 15 | * entire media stream flowing to the receiver, including media forwarded 16 | * from the opposite side of the call, as well as media produced by the 17 | * media_player. 18 | */ 19 | 20 | struct audio_player; 21 | struct call_media; 22 | 23 | bool audio_player_setup(struct call_media *, const rtp_payload_type *, 24 | unsigned int size_ms, unsigned int delay_ms, str_case_value_ht codec_set); 25 | void audio_player_activate(struct call_media *); 26 | void audio_player_free(struct call_media *); 27 | 28 | void audio_player_start(struct call_media *); 29 | void audio_player_stop(struct call_media *); 30 | bool audio_player_is_active(struct call_media *); 31 | bool audio_player_pt_match(struct call_media *, const rtp_payload_type *); 32 | 33 | void audio_player_add_frame(struct audio_player *, uint32_t ssrc, AVFrame *); 34 | 35 | #else 36 | 37 | INLINE void audio_player_start(struct call_media *m) { } 38 | INLINE void audio_player_free(struct call_media *m) { } 39 | INLINE void audio_player_stop(struct call_media *m) { } 40 | INLINE void audio_player_activate(struct call_media *m) { } 41 | 42 | #endif 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /include/cdr.h: -------------------------------------------------------------------------------- 1 | #ifndef CDR_H_ 2 | #define CDR_H_ 3 | 4 | #include "helpers.h" 5 | #include "types.h" 6 | 7 | enum tag_type; 8 | enum ng_opmode; 9 | 10 | const char *get_tag_type_text(enum tag_type t); 11 | const char *get_opmode_text(enum ng_opmode); 12 | void cdr_update_entry(call_t * c); 13 | 14 | #endif /* CDR_H_ */ 15 | -------------------------------------------------------------------------------- /include/cli.h: -------------------------------------------------------------------------------- 1 | #ifndef CLI_UDP_H_ 2 | #define CLI_UDP_H_ 3 | 4 | #include "socket.h" 5 | #include "obj.h" 6 | #include "tcp_listener.h" 7 | #include "types.h" 8 | 9 | struct cli { 10 | struct obj obj; 11 | 12 | struct streambuf_listener listener; 13 | }; 14 | 15 | struct cli_writer; 16 | struct call_monologue; 17 | 18 | struct cli_writer { 19 | size_t (*cw_printf)(struct cli_writer *, const char *, ...) __attribute__ ((format (printf, 2, 3))); 20 | void *ptr; 21 | call_t *call; 22 | struct call_monologue *ml; 23 | }; 24 | 25 | struct cli *cli_new(const endpoint_t *); 26 | 27 | void cli_handle(str *instr, struct cli_writer *); 28 | const char *cli_ng(ng_command_ctx_t *); 29 | 30 | #endif /* CLI_UDP_H_ */ 31 | -------------------------------------------------------------------------------- /include/control_ng_flags_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONTROL_NG_FLAGS_PARSER_H_ 2 | #define _CONTROL_NG_FLAGS_PARSER_H_ 3 | 4 | #include 5 | 6 | #include "bencode.h" 7 | #include "obj.h" 8 | #include "str.h" 9 | #include "call.h" 10 | #include "call_interfaces.h" 11 | 12 | /** 13 | * Parse flags in raw format and return bencode. 14 | * Syntax: 15 | * rtpp_flags: flag1=, flag2- ... 16 | */ 17 | void parse_rtpp_flags(const str * rtpp_flags, sdp_ng_flags *); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /include/control_tcp.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONTROL_H__ 2 | #define __CONTROL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "obj.h" 11 | #include "helpers.h" 12 | #include "socket.h" 13 | 14 | #define RE_TCP_RL_CMD 1 15 | #define RE_TCP_RL_CALLID 2 16 | #define RE_TCP_RL_STREAMS 3 17 | #define RE_TCP_RL_IP 4 18 | #define RE_TCP_RL_FROMDOM 5 19 | #define RE_TCP_RL_FROMTYPE 6 20 | #define RE_TCP_RL_TODOM 7 21 | #define RE_TCP_RL_TOTYPE 8 22 | #define RE_TCP_RL_AGENT 9 23 | #define RE_TCP_RL_INFO 10 24 | #define RE_TCP_D_CMD 11 25 | #define RE_TCP_D_CALLID 12 26 | #define RE_TCP_D_INFO 13 27 | #define RE_TCP_DIV_CMD 14 28 | 29 | struct control_tcp; 30 | struct streambuf_stream; 31 | 32 | struct control_tcp *control_tcp_new(const endpoint_t *); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /include/control_udp.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONTROL_UDP_H__ 2 | #define __CONTROL_UDP_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "obj.h" 10 | #include "helpers.h" 11 | #include "cookie_cache.h" 12 | #include "udp_listener.h" 13 | #include "socket.h" 14 | 15 | #define RE_UDP_COOKIE 1 16 | #define RE_UDP_UL_CMD 2 17 | #define RE_UDP_UL_FLAGS 3 18 | #define RE_UDP_UL_CALLID 4 19 | #define RE_UDP_UL_VIABRANCH 5 20 | #define RE_UDP_UL_ADDR4 6 21 | #define RE_UDP_UL_ADDR6 7 22 | #define RE_UDP_UL_PORT 8 23 | #define RE_UDP_UL_FROMTAG 9 24 | #define RE_UDP_UL_NUM 10 25 | #define RE_UDP_UL_TOTAG 11 26 | #define RE_UDP_DQ_CMD 12 27 | #define RE_UDP_DQ_FLAGS 13 28 | #define RE_UDP_DQ_CALLID 14 29 | #define RE_UDP_DQ_VIABRANCH 15 30 | #define RE_UDP_DQ_FROMTAG 16 31 | #define RE_UDP_DQ_TOTAG 17 32 | #define RE_UDP_V_CMD 18 33 | #define RE_UDP_V_FLAGS 19 34 | #define RE_UDP_V_PARMS 20 35 | 36 | struct control_udp { 37 | struct obj obj; 38 | 39 | struct cookie_cache cookie_cache; 40 | socket_t udp_listener; 41 | 42 | pcre2_code *parse_re; 43 | pcre2_code *fallback_re; 44 | }; 45 | 46 | 47 | 48 | 49 | 50 | struct control_udp *control_udp_new(const endpoint_t *); 51 | 52 | 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/cookie_cache.h: -------------------------------------------------------------------------------- 1 | #ifndef _COOKIE_CACHE_H_ 2 | #define _COOKIE_CACHE_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "helpers.h" 8 | #include "str.h" 9 | #include "control_ng.h" 10 | #include "bencode.h" 11 | 12 | struct cookie_cache_state { 13 | bencode_buffer_t buffer; 14 | GHashTable *in_use; 15 | GHashTable *cookies; 16 | }; 17 | 18 | typedef struct cache_entry { 19 | str reply; 20 | str callid; 21 | enum ng_opmode command; 22 | } cache_entry; 23 | 24 | INLINE cache_entry *cache_entry_dup(const cache_entry *s) { 25 | if (!s) 26 | return NULL; 27 | cache_entry *r; 28 | r = malloc(sizeof(*r)); 29 | r->reply = str_dup_str(&s->reply); 30 | r->command = s->command; 31 | r->callid = str_dup_str(&s->callid); 32 | return r; 33 | } 34 | INLINE void cache_entry_free(void *p) { 35 | cache_entry *s = p; 36 | if (!s) 37 | return; 38 | free(s->reply.s); 39 | free(s->callid.s); 40 | free(s); 41 | } 42 | struct cookie_cache { 43 | mutex_t lock; 44 | cond_t cond; 45 | struct cookie_cache_state current, old; 46 | int64_t swap_time_us; 47 | }; 48 | 49 | void cookie_cache_init(struct cookie_cache *); 50 | cache_entry *cookie_cache_lookup(struct cookie_cache *, const str *); 51 | void cookie_cache_insert(struct cookie_cache *, const str *, const struct cache_entry *); 52 | void cookie_cache_remove(struct cookie_cache *, const str *); 53 | void cookie_cache_cleanup(struct cookie_cache *); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/counter_stats_fields.inc: -------------------------------------------------------------------------------- 1 | #include "common_counter_stats_fields.inc" 2 | F(packets_user) 3 | F(bytes_user) 4 | F(errors_user) 5 | FA(ng_commands, OP_COUNT) 6 | F(timeout_sess) 7 | F(managed_sess) 8 | F(foreign_sess) 9 | F(rejected_sess) 10 | F(silent_timeout_sess) 11 | F(offer_timeout_sess) 12 | F(final_timeout_sess) 13 | F(regular_term_sess) 14 | F(forced_term_sess) 15 | F(nopacket_relayed_sess) 16 | F(oneway_stream_sess) 17 | F(call_duration) 18 | F(call_duration2) 19 | F(total_calls_duration_intv) 20 | F(rtp_duplicates) 21 | F(rtp_skips) 22 | F(rtp_seq_resets) 23 | F(rtp_reordered) 24 | -------------------------------------------------------------------------------- /include/gauge_stats_fields.inc: -------------------------------------------------------------------------------- 1 | F(total_sessions) 2 | F(foreign_sessions) 3 | F(transcoded_media) 4 | F(ipv4_sessions) 5 | F(ipv6_sessions) 6 | F(mixed_sessions) 7 | F(userspace_streams) 8 | F(kernel_only_streams) 9 | F(kernel_user_streams) 10 | F(media_cache) 11 | F(player_cache) 12 | -------------------------------------------------------------------------------- /include/graphite.h: -------------------------------------------------------------------------------- 1 | #ifndef GRAPHITE_H_ 2 | #define GRAPHITE_H_ 3 | 4 | #include "call.h" 5 | 6 | enum connection_state { 7 | STATE_DISCONNECTED = 0, 8 | STATE_IN_PROGRESS, 9 | STATE_CONNECTED, 10 | }; 11 | 12 | extern int64_t rtpe_latest_graphite_interval_start; 13 | 14 | extern struct global_stats_counter rtpe_stats_graphite_diff; // per-interval increases 15 | extern struct global_rate_min_max rtpe_rate_graphite_min_max; // running min/max, reset when graphite runs 16 | extern struct global_rate_min_max_avg rtpe_rate_graphite_min_max_avg_sampled; // updated once per graphite run 17 | 18 | extern struct global_gauge_min_max rtpe_gauge_graphite_min_max; // running min/max, reset when graphite runs 19 | extern struct global_gauge_min_max rtpe_gauge_graphite_min_max_sampled; // updated once per graphite run 20 | 21 | extern struct global_sampled_min_max rtpe_sampled_graphite_min_max; // running min/max, reset when graphite runs 22 | extern struct global_sampled_min_max rtpe_sampled_graphite_min_max_sampled; // updated once per graphite run 23 | extern struct global_sampled_avg rtpe_sampled_graphite_avg; // updated once per graphite run 24 | 25 | 26 | void set_prefix(char* prefix); 27 | void free_prefix(void); 28 | void graphite_loop(void *d); 29 | void set_latest_graphite_interval_start(int64_t tv); 30 | void set_graphite_interval_tv(int64_t tv); 31 | 32 | GString *print_graphite_data(void); 33 | 34 | 35 | INLINE bool graphite_is_enabled(void) { 36 | return !is_addr_unspecified(&rtpe_config.graphite_ep.address); 37 | } 38 | 39 | #endif /* GRAPHITE_H_ */ 40 | -------------------------------------------------------------------------------- /include/homer.h: -------------------------------------------------------------------------------- 1 | #ifndef __HOMER_H__ 2 | #define __HOMER_H__ 3 | 4 | #include "socket.h" 5 | 6 | #define PROTO_RTCP_JSON 0x05 7 | 8 | void homer_sender_init(const endpoint_t *, int, int); 9 | int homer_send(GString *, const str *, const endpoint_t *, const endpoint_t *, int64_t, int); 10 | int has_homer(void); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /include/iptables.h: -------------------------------------------------------------------------------- 1 | #ifndef _IPTABLES_H_ 2 | #define _IPTABLES_H_ 3 | 4 | #include "socket.h" 5 | #include "str.h" 6 | 7 | void iptables_init(void); 8 | extern int (*iptables_add_rule)(const socket_t *local_sock, const str *comment); 9 | extern int (*iptables_del_rule)(const socket_t *local_sock); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /include/janus.h: -------------------------------------------------------------------------------- 1 | #ifndef __JANUS_H__ 2 | #define __JANUS_H__ 3 | 4 | struct websocket_conn; 5 | struct websocket_message; 6 | struct janus_session; 7 | struct call_monologue; 8 | struct call_media; 9 | 10 | void janus_init(void); 11 | void janus_free(void); 12 | 13 | const char *websocket_janus_process(struct websocket_message *wm); 14 | const char *websocket_janus_get(struct websocket_message *wm); 15 | const char *websocket_janus_post(struct websocket_message *wm); 16 | void janus_detach_websocket(struct janus_session *session, struct websocket_conn *wc); 17 | void janus_rtc_up(struct call_monologue *); 18 | void janus_media_up(struct call_media *); 19 | 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /include/jitter_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef _JITTER_BUFFER_H_ 2 | #define _JITTER_BUFFER_H_ 3 | 4 | #include "auxlib.h" 5 | #include "socket.h" 6 | #include "timerthread.h" 7 | #include "media_socket.h" 8 | 9 | struct jb_packet; 10 | struct media_packet; 11 | 12 | struct jb_packet { 13 | struct timerthread_queue_entry ttq_entry; 14 | char *buf; 15 | struct media_packet mp; 16 | }; 17 | 18 | struct jitter_buffer { 19 | struct timerthread_queue ttq; 20 | mutex_t lock; 21 | unsigned long first_send_ts; 22 | int64_t first_send; 23 | int64_t prev_seq_ts; 24 | unsigned int first_seq; 25 | unsigned int prev_seq; 26 | unsigned int rtptime_delta; 27 | unsigned int next_exp_seq; 28 | unsigned int cont_frames; 29 | unsigned int cont_miss; 30 | unsigned int clock_rate; 31 | unsigned int payload_type; 32 | unsigned int num_resets; 33 | unsigned int initial_pkts; 34 | unsigned int ssrc; 35 | unsigned int dtmf_mult_factor; 36 | int buffer_len; 37 | int clock_drift_val; 38 | call_t *call; 39 | int disabled; 40 | }; 41 | 42 | void jitter_buffer_init(void); 43 | void jitter_buffer_init_free(void); 44 | 45 | struct jitter_buffer *jitter_buffer_new(call_t *); 46 | void jitter_buffer_free(struct jitter_buffer **); 47 | 48 | int buffer_packet(struct media_packet *mp, const str *s); 49 | void jb_packet_free(struct jb_packet **jbp); 50 | 51 | void jitter_buffer_launch(void); 52 | 53 | INLINE void jb_put(struct jitter_buffer **jb) { 54 | if (!*jb) 55 | return; 56 | obj_put(&(*jb)->ttq.tt_obj); 57 | *jb = NULL; 58 | } 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /include/kernel.h: -------------------------------------------------------------------------------- 1 | #ifndef __KERNEL_H__ 2 | #define __KERNEL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "containers.h" 9 | #include "auxlib.h" 10 | 11 | #include "xt_RTPENGINE.h" 12 | 13 | #define UNINIT_IDX ((unsigned int) -1) 14 | 15 | struct rtpengine_target_info; 16 | struct rtpengine_destination_info; 17 | struct re_address; 18 | struct rtpengine_ssrc_stats; 19 | 20 | struct kernel_interface { 21 | unsigned int table; 22 | int fd; 23 | bool is_open; 24 | bool is_wanted; 25 | bool use_player; 26 | }; 27 | extern struct kernel_interface kernel; 28 | 29 | 30 | 31 | bool kernel_setup_table(unsigned int); 32 | bool kernel_init_table(void); 33 | void kernel_shutdown_table(void); 34 | 35 | void kernel_add_stream(struct rtpengine_target_info *); 36 | void kernel_add_destination(struct rtpengine_destination_info *); 37 | bool kernel_del_stream(struct rtpengine_command_del_target *); 38 | 39 | unsigned int kernel_add_call(const char *id); 40 | void kernel_del_call(unsigned int); 41 | 42 | unsigned int kernel_add_intercept_stream(unsigned int call_idx, const char *id); 43 | 44 | bool kernel_init_player(int num_media, int num_sessions); 45 | unsigned int kernel_get_packet_stream(void); 46 | bool kernel_add_stream_packet(unsigned int, const char *, size_t, unsigned long ms, uint32_t ts, uint32_t dur); 47 | unsigned int kernel_start_stream_player(struct rtpengine_play_stream_info *); 48 | bool kernel_stop_stream_player(unsigned int idx); 49 | bool kernel_free_packet_stream(unsigned int); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /include/load.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOAD_H_ 2 | #define _LOAD_H_ 3 | 4 | extern int load_average; // times 100 5 | extern int cpu_usage; // times 100 6 | 7 | enum thread_looper_action load_thread(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /include/mqtt.h: -------------------------------------------------------------------------------- 1 | #ifndef _MQTT_H_ 2 | #define _MQTT_H_ 3 | 4 | #include 5 | 6 | #include "main.h" 7 | #include "types.h" 8 | 9 | struct call_media; 10 | 11 | #ifdef HAVE_MQTT 12 | 13 | 14 | int mqtt_init(void); 15 | void mqtt_loop(void *); 16 | int mqtt_publish_scope(void); 17 | void mqtt_publish(char *); 18 | void mqtt_timer_run_media(call_t *, struct call_media *); 19 | void mqtt_timer_run_call(call_t *); 20 | void mqtt_timer_run_global(void); 21 | void mqtt_timer_run_summary(void); 22 | 23 | 24 | #else 25 | 26 | #include "compat.h" 27 | 28 | INLINE int mqtt_init(void) { return 0; } 29 | INLINE void mqtt_publish(char *s) { } 30 | INLINE int mqtt_publish_scope(void) { return MPS_NONE; }; 31 | INLINE void mqtt_timer_run_media(call_t *c, struct call_media *m) { } 32 | INLINE void mqtt_timer_run_call(call_t *c) { } 33 | INLINE void mqtt_timer_run_global(void) { } 34 | INLINE void mqtt_timer_run_summary(void) { } 35 | 36 | #endif 37 | #endif 38 | -------------------------------------------------------------------------------- /include/nftables.h: -------------------------------------------------------------------------------- 1 | #ifndef _NFTABLES_H_ 2 | #define _NFTABLES_H_ 3 | 4 | #include 5 | 6 | typedef struct { 7 | int table; 8 | bool append; 9 | int family; 10 | } nftables_args; 11 | 12 | const char *nftables_setup(const char *chain, const char *base_chain, nftables_args); 13 | const char *nftables_shutdown(const char *chain, const char *base_chain, nftables_args); 14 | int nftables_check(const char *chain, const char *base_chain, nftables_args); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/ng_client.h: -------------------------------------------------------------------------------- 1 | #ifndef _NG_CLIENT_H_ 2 | #define _NG_CLIENT_H_ 3 | 4 | #include "types.h" 5 | #include "bencode.h" 6 | 7 | void ng_client_init(void); 8 | void ng_client_cleanup(void); 9 | 10 | bencode_item_t *ng_client_request(const endpoint_t *dst, const str *req, bencode_buffer_t *); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /include/rtcp.h: -------------------------------------------------------------------------------- 1 | #ifndef _RTCP_H_ 2 | #define _RTCP_H_ 3 | 4 | #include 5 | 6 | #include "str.h" 7 | #include "call.h" 8 | #include "media_socket.h" 9 | 10 | struct crypto_context; 11 | struct rtcp_packet; 12 | struct rtcp_handler; 13 | struct call_monologue; 14 | 15 | 16 | extern struct rtcp_handler *rtcp_transcode_handler; 17 | extern struct rtcp_handler *rtcp_sink_handler; 18 | 19 | 20 | int rtcp_avp2savp(str *, struct crypto_context *, struct ssrc_entry_call *); 21 | int rtcp_savp2avp(str *, struct crypto_context *, struct ssrc_entry_call *); 22 | 23 | int rtcp_payload(struct rtcp_packet **out, str *p, const str *s); 24 | 25 | int rtcp_parse(GQueue *q, struct media_packet *); 26 | void rtcp_list_free(GQueue *q); 27 | 28 | rtcp_filter_func rtcp_avpf2avp_filter; 29 | 30 | void rtcp_init(void); 31 | 32 | 33 | void rtcp_send_report(struct call_media *media, struct ssrc_entry_call *ssrc_out); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /include/rtp.h: -------------------------------------------------------------------------------- 1 | #ifndef _RTP_H_ 2 | #define _RTP_H_ 3 | 4 | #include 5 | 6 | #include "str.h" 7 | #include "types.h" 8 | 9 | struct crypto_context; 10 | struct rtp_header; 11 | struct ssrc_hash; 12 | struct ssrc_entry_call; 13 | struct codec_store; 14 | 15 | typedef GString crypto_debug_string; 16 | 17 | const rtp_payload_type *get_rtp_payload_type(unsigned int, struct codec_store *); 18 | 19 | int rtp_avp2savp(str *, struct crypto_context *, struct ssrc_entry_call *); 20 | int rtp_savp2avp(str *, struct crypto_context *, struct ssrc_entry_call *); 21 | 22 | int rtp_update_index(str *, struct packet_stream *, struct ssrc_entry_call *); 23 | 24 | void rtp_append_mki(str *s, struct crypto_context *c, crypto_debug_string *); 25 | int srtp_payloads(str *to_auth, str *to_decrypt, str *auth_tag, str *mki, 26 | int auth_len, int mki_len, 27 | const str *packet, const str *payload); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/sampled_stats_fields.inc: -------------------------------------------------------------------------------- 1 | FA(ng_command_times, OP_COUNT) 2 | F(mos) 3 | F(jitter) 4 | F(rtt_e2e) 5 | F(rtt_dsct) 6 | F(packetloss) 7 | F(jitter_measured) 8 | -------------------------------------------------------------------------------- /include/stun.h: -------------------------------------------------------------------------------- 1 | #ifndef _STUN_H_ 2 | #define _STUN_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "compat.h" 9 | #include "call.h" 10 | #include "str.h" 11 | #include "socket.h" 12 | 13 | #define STUN_COOKIE 0x2112A442UL 14 | 15 | struct stun_attrs { 16 | str username; 17 | char *msg_integrity_attr; 18 | str msg_integrity; 19 | uint32_t priority; 20 | char *fingerprint_attr; 21 | uint32_t fingerprint; 22 | uint64_t tiebreaker; 23 | endpoint_t mapped; 24 | unsigned int error_code; 25 | unsigned int use:1, 26 | controlled:1, 27 | controlling:1; 28 | }; 29 | 30 | 31 | INLINE int is_stun(const str *s) { 32 | const unsigned char *b = (const void *) s->s; 33 | const uint32_t *u; 34 | 35 | if (s->len < 20) 36 | return 0; 37 | if ((b[0] & 0xc0) != 0x00) 38 | return 0; 39 | if ((b[3] & 0x3) != 0x0) 40 | return 0; 41 | u = (const void *) &b[4]; 42 | if (*u != htonl(STUN_COOKIE)) 43 | return 0; 44 | 45 | return 1; 46 | } 47 | 48 | 49 | int stun(const str *, stream_fd *, const endpoint_t *); 50 | 51 | int stun_binding_request(const endpoint_t *dst, uint32_t transaction[3], str *pwd, 52 | str ufrags[2], int controlling, uint64_t tiebreaker, uint32_t priority, 53 | socket_t *, int); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/tcp_listener.h: -------------------------------------------------------------------------------- 1 | #ifndef _TCP_LISTENER_H_ 2 | #define _TCP_LISTENER_H_ 3 | 4 | #include "socket.h" 5 | #include "obj.h" 6 | #include "helpers.h" 7 | #include "containers.h" 8 | 9 | struct obj; 10 | struct streambuf_callback; 11 | struct streambuf_stream; 12 | 13 | TYPED_GHASHTABLE_PROTO(tcp_streams_ht, struct streambuf_stream, struct streambuf_stream) 14 | 15 | 16 | typedef void (*tcp_listener_callback_t)(struct obj *p, socket_t *sock, char *addr, socket_t *); 17 | typedef void (*streambuf_callback_t)(struct streambuf_stream *); 18 | 19 | struct streambuf_listener { 20 | socket_t listener; 21 | mutex_t lock; 22 | tcp_streams_ht streams; 23 | }; 24 | struct streambuf_stream { 25 | struct obj obj; 26 | socket_t sock; 27 | struct streambuf_listener *listener; 28 | struct streambuf_callback *cb; 29 | struct obj *parent; 30 | char *addr; 31 | struct streambuf *inbuf, 32 | *outbuf; 33 | 34 | }; 35 | 36 | int streambuf_listener_init(struct streambuf_listener *listener, const endpoint_t *ep, 37 | streambuf_callback_t newconn_func, 38 | streambuf_callback_t newdata_func, 39 | streambuf_callback_t closed_func, 40 | struct obj *obj); 41 | void streambuf_listener_shutdown(struct streambuf_listener *); 42 | 43 | void streambuf_stream_close(struct streambuf_stream *); 44 | void streambuf_stream_shutdown(struct streambuf_stream *); 45 | 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /include/udp_listener.h: -------------------------------------------------------------------------------- 1 | #ifndef _UDP_LISTENER_H_ 2 | #define _UDP_LISTENER_H_ 3 | 4 | #include 5 | 6 | #include "poller.h" 7 | #include "str.h" 8 | #include "socket.h" 9 | #include "obj.h" 10 | #include "call.h" 11 | 12 | #define MAX_UDP_LENGTH 0xffff 13 | 14 | struct udp_buffer { 15 | struct obj obj; 16 | char buf[MAX_UDP_LENGTH + RTP_BUFFER_TAIL_ROOM + RTP_BUFFER_HEAD_ROOM + 1]; 17 | str str; 18 | endpoint_t sin; 19 | sockaddr_t local_addr; 20 | char addr[64]; 21 | socket_t *listener; 22 | }; 23 | 24 | typedef void (*udp_listener_callback_t)(struct obj *p, struct udp_buffer *); 25 | 26 | int udp_listener_init(socket_t *, const endpoint_t *, udp_listener_callback_t, struct obj *); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/websocket.h: -------------------------------------------------------------------------------- 1 | #ifndef __WEBSOCKET_H__ 2 | #define __WEBSOCKET_H__ 3 | 4 | #include 5 | 6 | #include "str.h" 7 | 8 | struct websocket_conn; 9 | struct websocket_message; 10 | enum lws_write_protocol; 11 | struct janus_session; 12 | 13 | typedef const char *(*websocket_message_func_t)(struct websocket_message *); 14 | 15 | 16 | struct websocket_message { 17 | struct websocket_conn *wc; 18 | char *uri; 19 | enum { 20 | M_UNKNOWN = 0, 21 | M_WEBSOCKET, 22 | M_GET, 23 | M_POST, 24 | M_OPTIONS, 25 | } method; 26 | enum { 27 | CT_UNKNOWN = 0, 28 | CT_JSON, 29 | CT_NG, 30 | CT_TEXT, 31 | } content_type; 32 | GString *body; 33 | 34 | websocket_message_func_t func; 35 | }; 36 | 37 | 38 | int websocket_init(void); 39 | void websocket_start(void); 40 | void websocket_stop(void); 41 | 42 | // adds data to output buffer (can be null) and triggers specified response: http or binary websocket 43 | //void websocket_write_http_len(struct websocket_conn *wc, const char *msg, size_t len); 44 | //void websocket_write_http(struct websocket_conn *wc, const char *msg); 45 | void websocket_write_text(struct websocket_conn *wc, const char *msg); 46 | //void websocket_write_binary(struct websocket_conn *wc, const char *msg, size_t len); 47 | 48 | // single shot HTTP response 49 | void websocket_http_complete(struct websocket_conn *wc, int status, const char *content_type, 50 | ssize_t content_length, const char *content); 51 | 52 | // write HTTP response headers 53 | void websocket_http_response(struct websocket_conn *wc, int status, const char *content_type, 54 | ssize_t content_length); 55 | 56 | // mark a janus session as owned by this transport 57 | void websocket_conn_add_session(struct websocket_conn *, struct janus_session *); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /kernel-module/.gitignore: -------------------------------------------------------------------------------- 1 | .tmp_versions 2 | *.o 3 | *.ko 4 | *.mod.c 5 | modules.order 6 | Module.symvers 7 | rtpengine.mk 8 | .*.cmd 9 | xt_RTPENGINE.mod 10 | rtpengine-kmod.mk 11 | -------------------------------------------------------------------------------- /kernel-module/Makefile: -------------------------------------------------------------------------------- 1 | KSRC ?= /lib/modules/$(shell uname -r)/build 2 | KBUILD := $(KSRC) 3 | M ?= $(CURDIR) 4 | 5 | export M 6 | 7 | include $(M)/rtpengine-kmod.mk 8 | 9 | ccflags-y += -DRTPENGINE_VERSION="\"$(RTPENGINE_VERSION)\"" 10 | 11 | obj-m += xt_RTPENGINE.o 12 | 13 | .PHONY: modules clean install 14 | 15 | modules: 16 | $(MAKE) -C $(KBUILD) M=$(CURDIR) O=$(KBUILD) modules 17 | 18 | clean: 19 | $(MAKE) -C $(KBUILD) M=$(CURDIR) clean || true 20 | rm -f rtpengine-kmod.mk 21 | 22 | install: 23 | install -D xt_RTPENGINE.ko $(DESTDIR)/lib/modules/$(shell uname -r)/updates/xt_RTPENGINE.ko 24 | depmod -a 25 | 26 | $(M)/rtpengine-kmod.mk: 27 | $(M)/gen-rtpengine-kmod-flags >$@ 28 | -------------------------------------------------------------------------------- /kernel-module/common_counter_stats_fields.inc: -------------------------------------------------------------------------------- 1 | F(packets_kernel) 2 | F(bytes_kernel) 3 | F(errors_kernel) 4 | F(packets_lost) 5 | -------------------------------------------------------------------------------- /kernel-module/common_stats.h: -------------------------------------------------------------------------------- 1 | #ifndef _RTPE_COMMON_STATS_H_ 2 | #define _RTPE_COMMON_STATS_H_ 3 | 4 | 5 | #ifdef __KERNEL__ 6 | typedef atomic64_t atomic64; 7 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,1,0) 8 | static_assert(sizeof(atomic64_t) == sizeof(int64_t), "atomic64_t != int64_t"); 9 | static_assert(sizeof(atomic_t) == sizeof(int), "atomic_t != int"); 10 | // else: hope for the best 11 | #endif 12 | #else 13 | typedef int atomic_t; 14 | #endif 15 | 16 | 17 | struct interface_counter_stats_dir { 18 | #define F(n) atomic64 n; 19 | #include "interface_counter_stats_fields_dir.inc" 20 | #undef F 21 | }; 22 | struct interface_counter_stats { 23 | #define F(n) atomic64 n; 24 | #include "interface_counter_stats_fields.inc" 25 | #undef F 26 | }; 27 | struct interface_sampled_stats_fields { 28 | #define F(n) atomic64 n; 29 | #include "interface_sampled_stats_fields.inc" 30 | #undef F 31 | }; 32 | struct interface_sampled_stats { 33 | struct interface_sampled_stats_fields sums; 34 | struct interface_sampled_stats_fields sums_squared; 35 | struct interface_sampled_stats_fields counts; 36 | }; 37 | struct interface_sampled_stats_avg { 38 | struct interface_sampled_stats_fields avg; 39 | struct interface_sampled_stats_fields stddev; 40 | }; 41 | struct interface_stats_block { 42 | struct interface_counter_stats_dir in, 43 | out; 44 | struct interface_counter_stats s; 45 | struct interface_sampled_stats sampled; 46 | }; 47 | 48 | struct stream_stats { 49 | atomic64 packets; 50 | atomic64 bytes; 51 | atomic64 errors; 52 | atomic64 last_packet_us; 53 | atomic_t tos; 54 | }; 55 | 56 | struct rtp_stats { 57 | unsigned int payload_type; 58 | uint32_t clock_rate; 59 | atomic64 packets; 60 | atomic64 bytes; 61 | atomic64 kernel_packets; 62 | atomic64 kernel_bytes; 63 | }; 64 | struct ssrc_stats { 65 | atomic64 packets; 66 | atomic64 bytes; 67 | atomic_t timestamp; 68 | atomic_t ext_seq; 69 | atomic_t rtcp_seq; 70 | uint32_t lost_bits; // sliding bitfield, [0] = ext_seq 71 | atomic_t total_lost; 72 | atomic_t transit; 73 | atomic_t jitter; 74 | atomic64 last_packet_us; 75 | atomic_t last_pt; 76 | }; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /kernel-module/gen-rtpengine-kmod-flags: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "${M}" ]; then 4 | M=$(pwd) 5 | fi 6 | 7 | if [ -z "${RTPENGINE_VERSION}" ]; then 8 | have_dpkg_parsechangelog=no 9 | if command -v dpkg-parsechangelog >/dev/null; then 10 | have_dpkg_parsechangelog=yes 11 | fi 12 | if [ -f "${M}/../debian/changelog" ]; then 13 | deb_changelog="${M}/../debian/changelog" 14 | else 15 | deb_changelog="${M}/debian/changelog" 16 | fi 17 | if [ "${have_dpkg_parsechangelog}" = yes ]; then 18 | deb_version="$(dpkg-parsechangelog -l"${deb_changelog}" | awk '/^Version: / {print $2}')" 19 | fi 20 | git_br_commit="git-$(cd "${M}" && git rev-parse --abbrev-ref --symbolic-full-name HEAD 2> /dev/null)-$(cd "${M}" && git rev-parse --short HEAD 2> /dev/null)" 21 | 22 | if [ "${have_dpkg_parsechangelog}" = yes ]; then 23 | RTPENGINE_VERSION+=" ${deb_version}" 24 | fi 25 | if [ "${git_br_commit}" != "git--" ]; then 26 | RTPENGINE_VERSION+=" ${git_br_commit}" 27 | fi 28 | 29 | if [ -z "${RTPENGINE_VERSION}" ]; then 30 | RTPENGINE_VERSION="undefined" 31 | fi 32 | echo "RTPENGINE_VERSION := ${RTPENGINE_VERSION}" 33 | fi 34 | -------------------------------------------------------------------------------- /kernel-module/interface_counter_stats_fields.inc: -------------------------------------------------------------------------------- 1 | F(packets_lost) 2 | F(duplicates) 3 | -------------------------------------------------------------------------------- /kernel-module/interface_counter_stats_fields_dir.inc: -------------------------------------------------------------------------------- 1 | F(packets) 2 | F(bytes) 3 | F(errors) 4 | -------------------------------------------------------------------------------- /kernel-module/interface_sampled_stats_fields.inc: -------------------------------------------------------------------------------- 1 | F(mos) 2 | F(jitter) 3 | F(rtt_e2e) 4 | F(rtt_dsct) 5 | F(packetloss) 6 | F(jitter_measured) 7 | -------------------------------------------------------------------------------- /kernel-module/kernel_counter_stats_fields.inc: -------------------------------------------------------------------------------- 1 | #include "common_counter_stats_fields.inc" 2 | -------------------------------------------------------------------------------- /kernel-module/other_counter_stats_fields.inc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/kernel-module/other_counter_stats_fields.inc -------------------------------------------------------------------------------- /kernel-module/xt_RTPENGINE.modules.load.d: -------------------------------------------------------------------------------- 1 | # Load xt_RTPENGINE kernel module at boot. 2 | xt_RTPENGINE 3 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean distclean 2 | 3 | .DEFAULT: 4 | true 5 | 6 | clean distclean: 7 | rm -f *.strhash.* 8 | -------------------------------------------------------------------------------- /lib/bitstr.h: -------------------------------------------------------------------------------- 1 | #ifndef _BITSTR_H_ 2 | #define _BITSTR_H_ 3 | 4 | #include "str.h" 5 | #include 6 | 7 | struct bitstr_s { 8 | str s; 9 | unsigned int bit_offset; // leading consumed bits 10 | }; 11 | typedef struct bitstr_s bitstr; 12 | 13 | INLINE void bitstr_init(bitstr *b, const str *s) { 14 | b->s = *s; 15 | b->bit_offset = 0; 16 | } 17 | 18 | INLINE int bitstr_shift_ret(bitstr *b, unsigned int bits, str *ret) { 19 | if (!bits) 20 | return 0; 21 | // check if we have enough 22 | if (bits > b->s.len * 8 - b->bit_offset) 23 | return -1; 24 | 25 | unsigned int to_copy = (bits + b->bit_offset + 7) / 8; 26 | 27 | if (ret) { 28 | assert(ret->len >= to_copy); 29 | ret->len = to_copy; 30 | memcpy(ret->s, b->s.s, to_copy); 31 | unsigned char *ret_s = (unsigned char *) ret->s; // avoid bitshifts on signed chars 32 | 33 | // we have to bit-shift the entire string if there was a leading offset 34 | if (b->bit_offset) { 35 | unsigned int left = bits; 36 | unsigned int c = 0; 37 | while (b->bit_offset + left > 8) { 38 | // enough to fill one output byte from two consecutive input bytes 39 | ret_s[c] <<= b->bit_offset; 40 | ret_s[c] |= ret_s[c + 1] >> (8 - b->bit_offset); 41 | if (left <= 8) { 42 | // final trailing bits overlapping bytes: truncate 43 | ret_s[c] &= 0xff << (8 - left); 44 | left = 0; 45 | ret->len--; 46 | } 47 | else 48 | left -= 8; 49 | c++; 50 | } 51 | if (left) { 52 | // last byte has the remainder 53 | ret_s[c] <<= b->bit_offset; 54 | ret_s[c] &= 0xff << (8 - left); 55 | } 56 | } 57 | else { 58 | // truncate last byte if needed 59 | unsigned int bits_left = bits % 8; 60 | if (bits_left) 61 | ret_s[to_copy - 1] &= 0xff << (8 - bits_left); 62 | } 63 | } 64 | 65 | b->bit_offset += bits; 66 | unsigned int int_bytes = b->bit_offset / 8; 67 | int shift_ret = str_shift(&b->s, int_bytes); 68 | assert(shift_ret == 0); 69 | (void) shift_ret; 70 | b->bit_offset -= int_bytes * 8; 71 | 72 | return 0; 73 | } 74 | 75 | INLINE int bitstr_shift(bitstr *b, unsigned int bits) { 76 | return bitstr_shift_ret(b, bits, NULL); 77 | } 78 | 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /lib/bufferpool.h: -------------------------------------------------------------------------------- 1 | #ifndef _BUFFERPOOL_H_ 2 | #define _BUFFERPOOL_H_ 3 | 4 | #include "obj.h" 5 | 6 | #define BUFFERPOOL_ALIGNMENT (sizeof(void *)) // bytes 7 | #define BUFFERPOOL_ALIGN(x) (((x + BUFFERPOOL_ALIGNMENT - 1) / BUFFERPOOL_ALIGNMENT) * BUFFERPOOL_ALIGNMENT) 8 | 9 | #define BUFFERPOOL_SHARD_SIZE (1LL<<24) // 16 MB, must be a power of two 10 | #define BUFFERPOOL_OVERHEAD BUFFERPOOL_ALIGN(sizeof(void *)) // storage space not available 11 | 12 | #define BUFFERPOOL_BOTTOM_MASK (BUFFERPOOL_SHARD_SIZE - 1) 13 | #define BUFFERPOOL_TOP_MASK (~BUFFERPOOL_BOTTOM_MASK) 14 | 15 | struct bufferpool; 16 | struct bpool_shard; 17 | 18 | void bufferpool_init(void); 19 | void bufferpool_cleanup(void); 20 | 21 | struct bufferpool *bufferpool_new(void *(*alloc)(void), void (*dealloc)(void *)); 22 | void bufferpool_destroy(struct bufferpool *); 23 | 24 | void *bufferpool_alloc(struct bufferpool *bp, size_t len); 25 | void *bufferpool_reserve(struct bufferpool *bp, unsigned int refs, unsigned int (*recycle)(void *), void *arg); 26 | void *bufferpool_ref(void *); 27 | void bufferpool_unref(void *); 28 | void bufferpool_release(void *); // remove all refs 29 | 30 | INLINE void *bufferpool_alloc0(struct bufferpool *bp, size_t len) { 31 | void *ret = bufferpool_alloc(bp, len); 32 | if (!ret) 33 | return NULL; 34 | memset(ret, 0, len); 35 | return ret; 36 | } 37 | 38 | void *bufferpool_aligned_alloc(void); 39 | void bufferpool_aligned_free(void *); 40 | 41 | typedef char bp_char; 42 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(bp_char, bufferpool_unref); 43 | typedef char bp_void; 44 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(bp_void, bufferpool_unref); 45 | 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /lib/common.Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := all 2 | 3 | include ../lib/lib.Makefile 4 | 5 | CFLAGS += -fPIE 6 | LDFLAGS += -pie 7 | LDLIBS += -latomic 8 | 9 | all: $(TARGET) $(MANS) 10 | 11 | $(TARGET): $(OBJS) Makefile 12 | $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJS) $(LDLIBS) 13 | 14 | debug: 15 | $(MAKE) DBG=yes all 16 | 17 | BUILD_TEST_ALTS = fix_frame_channel_layout.h dtmf_rx_fillin.h spandsp_logging.h 18 | 19 | clean: 20 | rm -f $(OBJS) $(TARGET) $(LIBSRCS) $(LIBASM) $(DAEMONSRCS) $(MANS) $(ADD_CLEAN) core core.* 21 | rm -f $(BUILD_TEST_ALTS) $(BUILD_TEST_ALTS:.h=-test.c) $(BUILD_TEST_ALTS:.h=-test) *.strhash.c $(HASHSRCS) 22 | 23 | install: 24 | 25 | $(OBJS): Makefile ../include/* ../lib/*.h ../kernel-module/*.h 26 | 27 | $(LIBSRCS): $(patsubst %,../lib/%,$(LIBSRCS)) 28 | ( echo '/******** GENERATED FILE ********/' && \ 29 | echo '#line 1' && \ 30 | cat ../lib/"$@" ) > "$@" 31 | 32 | $(LIBASM): $(patsubst %,../lib/%,$(LIBASM)) 33 | ( echo '/******** GENERATED FILE ********/' && \ 34 | echo '#line 1' && \ 35 | cat ../lib/"$@" ) > "$@" 36 | 37 | $(DAEMONSRCS) $(HASHSRCS): $(patsubst %,../daemon/%,$(DAEMONSRCS)) $(patsubst %,../daemon/%,$(HASHSRCS)) 38 | ( echo '/******** GENERATED FILE ********/' && \ 39 | echo '#line 1' && \ 40 | cat ../daemon/"$@" ) > "$@" 41 | 42 | %.8: ../docs/%.md 43 | cat "$<" | sed '/^# /d; s/^##/#/' | \ 44 | pandoc -s -t man \ 45 | -M "footer:$(RTPENGINE_VERSION)" \ 46 | -M "date:$(BUILD_DATE)" \ 47 | -o "$@" 48 | 49 | resample.c codeclib.strhash.c mix.c packet.c: fix_frame_channel_layout.h 50 | 51 | ifeq ($(with_transcoding),yes) 52 | codec.c: dtmf_rx_fillin.h 53 | media_player.c codec.c test-resample.c: fix_frame_channel_layout.h 54 | endif 55 | 56 | t38.c: spandsp_logging.h 57 | 58 | %.strhash.c: %.c ../utils/const_str_hash 59 | ../utils/const_str_hash "$<" $(CFLAGS) < "$<" > "$@" 60 | 61 | $(BUILD_TEST_ALTS): $(wildcard ../lib/$(subst .h,-*,$(BUILD_TEST_ALTS))) 62 | ../utils/build_test_wrapper "$@" 2> /dev/null 63 | 64 | .PHONY: all debug clean install 65 | -------------------------------------------------------------------------------- /lib/compat.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMPAT_H__ 2 | #define __COMPAT_H__ 3 | 4 | #if __DEBUG 5 | # define INLINE static inline 6 | #else 7 | # define INLINE static inline __attribute__((always_inline)) 8 | #endif 9 | 10 | #if defined __has_attribute 11 | # define HAS_ATTR(x) __has_attribute(x) 12 | #else 13 | # define HAS_ATTR(x) 0 14 | #endif 15 | 16 | #if HAS_ATTR(access) 17 | # define ACCESS(...) __attribute__((access(__VA_ARGS__))) 18 | #else 19 | # define ACCESS(...) 20 | #endif 21 | 22 | 23 | #ifndef BENCODE_MALLOC 24 | #define BENCODE_MALLOC malloc 25 | #define BENCODE_FREE free 26 | #endif 27 | 28 | #include "str.h" 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /lib/deps.Makefile: -------------------------------------------------------------------------------- 1 | # Define build flags for used dependencies. 2 | 3 | $(top_srcdir)/config.mk: 4 | $(top_srcdir)/utils/gen-common-flags >$@ 5 | ifeq (,$(filter pkg.ngcp-rtpengine.nobcg729,${DEB_BUILD_PROFILES})) 6 | $(top_srcdir)/utils/gen-bcg729-flags >>$@ 7 | endif 8 | ifneq (,$(filter pkg.ngcp-rtpengine.codec-chain,${DEB_BUILD_PROFILES})) 9 | $(top_srcdir)/utils/gen-codec-chain-flags >>$@ 10 | endif 11 | 12 | include $(top_srcdir)/config.mk 13 | -------------------------------------------------------------------------------- /lib/dtmf_rx_fillin-01.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | -------------------------------------------------------------------------------- /lib/dtmf_rx_fillin-02.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "compat.h" 10 | 11 | INLINE void dtmf_rx_fillin(dtmf_rx_state_t *dsp, int n) { 12 | // stub 13 | } 14 | -------------------------------------------------------------------------------- /lib/dtmf_rx_fillin-test.c: -------------------------------------------------------------------------------- 1 | #include "dtmf_rx_fillin.h" 2 | int main(void) { 3 | dtmf_rx_state_t *dsp = NULL; 4 | dtmf_rx_fillin(dsp, 0); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /lib/dtmflib.h: -------------------------------------------------------------------------------- 1 | #ifndef _DTMFLIB_H_ 2 | #define _DTMFLIB_H_ 3 | 4 | #include 5 | #include 6 | 7 | 8 | struct telephone_event_payload { 9 | uint8_t event; 10 | #if G_BYTE_ORDER == G_LITTLE_ENDIAN 11 | unsigned volume:6; 12 | unsigned r:1; 13 | unsigned end:1; 14 | #elif G_BYTE_ORDER == G_BIG_ENDIAN 15 | unsigned end:1; 16 | unsigned r:1; 17 | unsigned volume:6; 18 | #else 19 | #error "byte order unknown" 20 | #endif 21 | uint16_t duration; 22 | } __attribute__ ((packed)); 23 | 24 | 25 | void dtmf_samples_int16_t_mono(void *buf, unsigned long offset, unsigned long num, unsigned int event, 26 | unsigned int volume, unsigned int sample_rate); 27 | 28 | 29 | void tone_samples_int16_t(int16_t *buf, unsigned long offset, unsigned long num, unsigned int freq, 30 | unsigned int volume, unsigned int sample_rate, unsigned int channels); 31 | void tone_samples_int32_t(int32_t *buf, unsigned long offset, unsigned long num, unsigned int freq, 32 | unsigned int volume, unsigned int sample_rate, unsigned int channels); 33 | void tone_samples_double(double *buf, unsigned long offset, unsigned long num, unsigned int freq, 34 | unsigned int volume, unsigned int sample_rate, unsigned int channels); 35 | void tone_samples_float(float *buf, unsigned long offset, unsigned long num, unsigned int freq, 36 | unsigned int volume, unsigned int sample_rate, unsigned int channels); 37 | 38 | void dtmf_samples_int16_t(int16_t *buf, unsigned long offset, unsigned long num, unsigned int event, 39 | unsigned int volume, unsigned int sample_rate, unsigned int channels); 40 | void dtmf_samples_int32_t(int32_t *buf, unsigned long offset, unsigned long num, unsigned int event, 41 | unsigned int volume, unsigned int sample_rate, unsigned int channels); 42 | void dtmf_samples_double(double *buf, unsigned long offset, unsigned long num, unsigned int event, 43 | unsigned int volume, unsigned int sample_rate, unsigned int channels); 44 | void dtmf_samples_float(float *buf, unsigned long offset, unsigned long num, unsigned int event, 45 | unsigned int volume, unsigned int sample_rate, unsigned int channels); 46 | 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /lib/fix_frame_channel_layout-01.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "compat.h" 4 | 5 | #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) 6 | // both `channel_layout` and `channels` are deprecated in favour of `ch_layout` 7 | #define CH_LAYOUT ch_layout 8 | #define CH_LAYOUT_T AVChannelLayout 9 | #define DEF_CH_LAYOUT(d,n) av_channel_layout_default(d,n) 10 | #define CH_LAYOUT_EQ(a,b) (av_channel_layout_compare(&(a),&(b)) == 0) 11 | #define SWR_ALLOC_SET_OPTS(a,b,c,d,e,f,g,h,i) swr_alloc_set_opts2(a,&(b),c,d,&(e),f,g,h,i) 12 | #define SET_CHANNELS(a,b) ((void)0) 13 | #define MONO_LAYOUT AV_CHANNEL_LAYOUT_MONO 14 | #define GET_CHANNELS(x) (x)->ch_layout.nb_channels 15 | #define CH_LAYOUT_EXTRACT_MASK(a,b) (1ULL << av_channel_layout_channel_from_index(&(a),b)) 16 | #define CH_LAYOUT_MASK(a) (a)->u.mask 17 | #define CH_LAYOUT_FROM_MASK(a,b) av_channel_layout_from_mask(a,b) 18 | #define CH_LAYOUT_PRINT(a,b) av_channel_layout_describe(&(a),b,sizeof(b)) 19 | #else 20 | #define CH_LAYOUT channel_layout 21 | #define CH_LAYOUT_T uint64_t 22 | #define DEF_CH_LAYOUT(d,n) *(d) = av_get_default_channel_layout(n) 23 | #define CH_LAYOUT_EQ(a,b) ((a) == (b)) 24 | #define SWR_ALLOC_SET_OPTS(a,b,c,d,e,f,g,h,i) *(a) = swr_alloc_set_opts(NULL,b,c,d,e,f,g,h,i) 25 | #define SET_CHANNELS(a,b) (a)->channels = (b) 26 | #define MONO_LAYOUT AV_CH_LAYOUT_MONO 27 | #define GET_CHANNELS(x) (x)->channels 28 | #define CH_LAYOUT_EXTRACT_MASK(a,b) av_channel_layout_extract_channel(a,b) 29 | #define CH_LAYOUT_MASK(a) (a) 30 | #define CH_LAYOUT_FROM_MASK(a,b) *(a) = (b) 31 | #define CH_LAYOUT_PRINT(a,b) snprintf(b, sizeof(b), "0x%" PRIx64, a) 32 | #endif 33 | 34 | INLINE void fix_frame_channel_layout(AVFrame *frame) { 35 | #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) 36 | return; 37 | #else 38 | if (frame->channel_layout) { 39 | #if LIBAVUTIL_VERSION_MAJOR < 56 40 | if (!frame->channels) 41 | frame->channels = av_frame_get_channels(frame); 42 | #endif 43 | return; 44 | } 45 | #if LIBAVUTIL_VERSION_MAJOR < 56 46 | frame->channel_layout = av_get_default_channel_layout(av_frame_get_channels(frame)); 47 | #else 48 | frame->channel_layout = av_get_default_channel_layout(frame->channels); 49 | #endif 50 | #endif 51 | } 52 | -------------------------------------------------------------------------------- /lib/fix_frame_channel_layout-02.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "compat.h" 4 | 5 | #define CH_LAYOUT channel_layout 6 | #define CH_LAYOUT_T uint64_t 7 | #define DEF_CH_LAYOUT(d,n) *(d) = av_get_default_channel_layout(n) 8 | #define CH_LAYOUT_EQ(a,b) ((a) == (b)) 9 | #define SWR_ALLOC_SET_OPTS(a,b,c,d,e,f,g,h,i) *(a) = swr_alloc_set_opts(NULL,b,c,d,e,f,g,h,i) 10 | #define SET_CHANNELS(a,b) (a)->channels = (b) 11 | #define MONO_LAYOUT AV_CH_LAYOUT_MONO 12 | #define GET_CHANNELS(x) (x)->channels 13 | #define CH_LAYOUT_EXTRACT_MASK(a,b) av_channel_layout_extract_channel(a,b) 14 | #define CH_LAYOUT_MASK(a) (a) 15 | #define CH_LAYOUT_FROM_MASK(a,b) *(a) = (b) 16 | #define CH_LAYOUT_PRINT(a,b) snprintf(b, sizeof(b), "0x%" PRIx64, a) 17 | 18 | INLINE void fix_frame_channel_layout(AVFrame *frame) { 19 | if (frame->channel_layout) { 20 | #if LIBAVUTIL_VERSION_MAJOR < 56 21 | if (!frame->channels) 22 | frame->channels = av_frame_get_channels(frame); 23 | #endif 24 | return; 25 | } 26 | #if LIBAVUTIL_VERSION_MAJOR < 56 27 | frame->channel_layout = av_get_default_channel_layout(av_frame_get_channels(frame)); 28 | #else 29 | frame->channel_layout = av_get_default_channel_layout(frame->channels); 30 | #endif 31 | } 32 | -------------------------------------------------------------------------------- /lib/fix_frame_channel_layout-03.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "compat.h" 4 | 5 | #define CH_LAYOUT channel_layout 6 | #define CH_LAYOUT_T uint64_t 7 | #define DEF_CH_LAYOUT(d,n) *(d) = av_get_default_channel_layout(n) 8 | #define CH_LAYOUT_EQ(a,b) ((a) == (b)) 9 | #define SWR_ALLOC_SET_OPTS(a,b,c,d,e,f,g,h,i) *(a) = swr_alloc_set_opts(NULL,b,c,d,e,f,g,h,i) 10 | #define SET_CHANNELS(a,b) (a)->channels = (b) 11 | #define MONO_LAYOUT AV_CH_LAYOUT_MONO 12 | #define GET_CHANNELS(x) (x)->channels 13 | #define CH_LAYOUT_EXTRACT_MASK(a,b) av_channel_layout_extract_channel(a,b) 14 | #define CH_LAYOUT_MASK(a) (a) 15 | #define CH_LAYOUT_FROM_MASK(a,b) *(a) = (b) 16 | #define CH_LAYOUT_PRINT(a,b) snprintf(b, sizeof(b), "0x%" PRIx64, a) 17 | 18 | INLINE void fix_frame_channel_layout(AVFrame *frame) { 19 | if (frame->channel_layout) { 20 | if (!frame->channels) 21 | frame->channels = av_frame_get_channels(frame); 22 | return; 23 | } 24 | frame->channel_layout = av_get_default_channel_layout(frame->channels); 25 | } 26 | -------------------------------------------------------------------------------- /lib/fix_frame_channel_layout-04.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "compat.h" 4 | 5 | #define CH_LAYOUT channel_layout 6 | #define CH_LAYOUT_T uint64_t 7 | #define DEF_CH_LAYOUT(d,n) *(d) = av_get_default_channel_layout(n) 8 | #define CH_LAYOUT_EQ(a,b) ((a) == (b)) 9 | #define SWR_ALLOC_SET_OPTS(a,b,c,d,e,f,g,h,i) *(a) = swr_alloc_set_opts(NULL,b,c,d,e,f,g,h,i) 10 | #define SET_CHANNELS(a,b) (a)->channels = (b) 11 | #define MONO_LAYOUT AV_CH_LAYOUT_MONO 12 | #define GET_CHANNELS(x) (x)->channels 13 | #define CH_LAYOUT_EXTRACT_MASK(a,b) av_channel_layout_extract_channel(a,b) 14 | #define CH_LAYOUT_MASK(a) (a) 15 | #define CH_LAYOUT_FROM_MASK(a,b) *(a) = (b) 16 | #define CH_LAYOUT_PRINT(a,b) snprintf(b, sizeof(b), "0x%" PRIx64, a) 17 | 18 | INLINE void fix_frame_channel_layout(AVFrame *frame) { 19 | if (frame->channel_layout) 20 | return; 21 | frame->channel_layout = av_get_default_channel_layout(av_frame_get_channels(frame)); 22 | } 23 | -------------------------------------------------------------------------------- /lib/fix_frame_channel_layout-05.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "compat.h" 4 | 5 | #define CH_LAYOUT channel_layout 6 | #define CH_LAYOUT_T uint64_t 7 | #define DEF_CH_LAYOUT(d,n) *(d) = av_get_default_channel_layout(n) 8 | #define CH_LAYOUT_EQ(a,b) ((a) == (b)) 9 | #define SWR_ALLOC_SET_OPTS(a,b,c,d,e,f,g,h,i) *(a) = swr_alloc_set_opts(NULL,b,c,d,e,f,g,h,i) 10 | #define SET_CHANNELS(a,b) (a)->channels = (b) 11 | #define MONO_LAYOUT AV_CH_LAYOUT_MONO 12 | #define GET_CHANNELS(x) (x)->channels 13 | #define CH_LAYOUT_EXTRACT_MASK(a,b) av_channel_layout_extract_channel(a,b) 14 | #define CH_LAYOUT_MASK(a) (a) 15 | #define CH_LAYOUT_FROM_MASK(a,b) *(a) = (b) 16 | #define CH_LAYOUT_PRINT(a,b) snprintf(b, sizeof(b), "0x%" PRIx64, a) 17 | 18 | INLINE void fix_frame_channel_layout(AVFrame *frame) { 19 | if (frame->channel_layout) 20 | return; 21 | frame->channel_layout = av_get_default_channel_layout(frame->channels); 22 | } 23 | -------------------------------------------------------------------------------- /lib/fix_frame_channel_layout-test.c: -------------------------------------------------------------------------------- 1 | #include "fix_frame_channel_layout.h" 2 | int main(void) { 3 | AVFrame *f = NULL; 4 | fix_frame_channel_layout(f); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /lib/lib.Makefile: -------------------------------------------------------------------------------- 1 | CC ?= gcc 2 | 3 | CFLAGS+= -DRTPENGINE_VERSION="\"$(RTPENGINE_VERSION)\"" 4 | 5 | CFLAGS+= $(CFLAGS_LIBSYSTEMD) 6 | LDLIBS+= $(LDLIBS_LIBSYSTEMD) 7 | 8 | # look for liburing 9 | ifeq (,$(filter pkg.ngcp-rtpengine.nouring,${DEB_BUILD_PROFILES})) 10 | CFLAGS+= $(CFLAGS_LIBURING) 11 | LDLIBS+= $(LDLIBS_LIBURING) 12 | endif 13 | 14 | ifeq ($(DBG),yes) 15 | CFLAGS+= -D__DEBUG=1 16 | endif 17 | 18 | # keep debugging symbols for backtrace_symbols() 19 | LDFLAGS += -rdynamic 20 | 21 | ifneq ($(DBG),yes) 22 | ifeq (,$(filter $(CFLAGS),-O0)) 23 | CFLAGS+= $(CFLAGS_DEFAULT) 24 | CPPFLAGS+= $(CPPFLAGS_DEFAULT) 25 | LDFLAGS+= $(LDFLAGS_DEFAULT) 26 | endif 27 | endif 28 | 29 | 30 | DATE_FMT = +%Y-%m-%d 31 | ifdef SOURCE_DATE_EPOCH 32 | BUILD_DATE ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null || date -u -r "$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null || date -u "$(DATE_FMT)") 33 | else 34 | BUILD_DATE ?= $(shell date "$(DATE_FMT)") 35 | endif 36 | -------------------------------------------------------------------------------- /lib/mix_in_x64_avx2.S: -------------------------------------------------------------------------------- 1 | #if defined(__linux__) && defined(__ELF__) 2 | .section .note.GNU-stack,"",%progbits 3 | #endif 4 | 5 | #if defined(__x86_64__) 6 | 7 | .global s16_mix_in_avx2 8 | 9 | .text 10 | 11 | # 16 bits in 256 bits = 16 samples at a time 12 | s16_mix_in_avx2: 13 | mov %rdx, %rax 14 | and $-16, %al # 16 samples at a time 15 | xor %rcx, %rcx 16 | loop: 17 | cmp %rax, %rcx 18 | jge remainder 19 | vmovdqu (%rdi,%rcx,2), %ymm0 # 16-bit size 20 | vpaddsw (%rsi,%rcx,2), %ymm0, %ymm1 21 | vmovdqu %ymm1, (%rdi,%rcx,2) # 16-bit size 22 | add $16, %rcx # 16 samples at a time 23 | jmp loop 24 | remainder: 25 | xor %r8, %r8 26 | xor %r9, %r9 27 | cmp %rdx, %rcx 28 | jge done 29 | mov (%rsi,%rcx,2), %r8w # 16-bit size 30 | mov (%rdi,%rcx,2), %r9w # 16-bit size 31 | movd %r8, %xmm0 32 | movd %r9, %xmm1 33 | paddsw %xmm0, %xmm1 34 | movd %xmm1, %r8 35 | mov %r8w, (%rdi,%rcx,2) # 16-bit size 36 | inc %rcx 37 | jmp remainder 38 | done: 39 | ret 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /lib/mix_in_x64_avx512bw.S: -------------------------------------------------------------------------------- 1 | #if defined(__linux__) && defined(__ELF__) 2 | .section .note.GNU-stack,"",%progbits 3 | #endif 4 | 5 | #if defined(__x86_64__) 6 | 7 | .global s16_mix_in_avx512 8 | 9 | .text 10 | 11 | # 16 bits in 512 bits = 32 samples at a time 12 | s16_mix_in_avx512: 13 | mov %rdx, %rax 14 | and $-32, %al # 32 samples at a time 15 | xor %rcx, %rcx 16 | loop: 17 | cmp %rax, %rcx 18 | jge remainder 19 | vmovdqu16 (%rdi,%rcx,2), %zmm0 # 16-bit size 20 | vpaddsw (%rsi,%rcx,2), %zmm0, %zmm1 21 | vmovdqu16 %zmm1, (%rdi,%rcx,2) # 16-bit size 22 | add $32, %rcx # 32 samples at a time 23 | jmp loop 24 | remainder: 25 | xor %r8, %r8 26 | xor %r9, %r9 27 | cmp %rdx, %rcx 28 | jge done 29 | mov (%rsi,%rcx,2), %r8w # 16-bit size 30 | mov (%rdi,%rcx,2), %r9w # 16-bit size 31 | movd %r8, %xmm0 32 | movd %r9, %xmm1 33 | paddsw %xmm0, %xmm1 34 | movd %xmm1, %r8 35 | mov %r8w, (%rdi,%rcx,2) # 16-bit size 36 | inc %rcx 37 | jmp remainder 38 | done: 39 | ret 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /lib/mix_in_x64_sse2.S: -------------------------------------------------------------------------------- 1 | #if defined(__linux__) && defined(__ELF__) 2 | .section .note.GNU-stack,"",%progbits 3 | #endif 4 | 5 | #if defined(__x86_64__) 6 | 7 | .global s16_mix_in_sse2 8 | 9 | .text 10 | 11 | # 16 bits in 128 bits = 8 samples at a time 12 | s16_mix_in_sse2: 13 | mov %rdx, %rax 14 | and $-8, %al # 8 samples at a time 15 | xor %rcx, %rcx 16 | loop: 17 | cmp %rax, %rcx 18 | jge remainder 19 | movdqu (%rdi,%rcx,2), %xmm0 # 16-bit size 20 | movdqu (%rsi,%rcx,2), %xmm1 # 16-bit size 21 | paddsw %xmm0, %xmm1 22 | movdqu %xmm1, (%rdi,%rcx,2) # 16-bit size 23 | add $8, %rcx # 8 samples at a time 24 | jmp loop 25 | remainder: 26 | xor %r8, %r8 27 | xor %r9, %r9 28 | cmp %rdx, %rcx 29 | jge done 30 | mov (%rsi,%rcx,2), %r8w # 16-bit size 31 | mov (%rdi,%rcx,2), %r9w # 16-bit size 32 | movd %r8, %xmm0 33 | movd %r9, %xmm1 34 | paddsw %xmm0, %xmm1 35 | movd %xmm1, %r8 36 | mov %r8w, (%rdi,%rcx,2) # 16-bit size 37 | inc %rcx 38 | jmp remainder 39 | done: 40 | ret 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /lib/mvr2s_x64_avx2.S: -------------------------------------------------------------------------------- 1 | #if defined(__linux__) && defined(__ELF__) 2 | .section .note.GNU-stack,"",%progbits 3 | #endif 4 | 5 | #if defined(__x86_64__) 6 | 7 | .global mvr2s_avx2 8 | 9 | .text 10 | 11 | # void mvr2s_avx2(float *in, const uint16_t len, int16_t *out); 12 | # convert float array to int16 array with rounding and int16 saturation 13 | mvr2s_avx2: 14 | vmovups mask(%rip), %ymm3 # mask for vpermd 15 | 16 | ldmxcsr csr(%rip) # set "round to nearest" 17 | 18 | mov %rsi, %rax 19 | and $-8, %al # 8 samples at a time 20 | 21 | xor %rcx, %rcx 22 | loop: 23 | cmp %rax, %rcx 24 | jge remainder 25 | 26 | vmovups (%rdi,%rcx,4), %ymm0 # load, 32-bit size 27 | 28 | # v8_float = {-4, -3.20000005, -1.70000005, -0.5, 0, 38000, -38000, 0}, 29 | # -> 30 | # v8_int32 = {-4, -3, -2, 0, 0, 38000, -38000, 0}, 31 | vcvtps2dq %ymm0, %ymm1 32 | 33 | # v8_int32 = {-4, -3, -2, 0, 0, 38000, -38000, 0}, 34 | # -> 35 | # v16_int16 = {-4, -3, -2, 0, -4, -3, -2, 0, 0, 32767, -32768, 0, 0, 32767, -32768, 0}, 36 | vpackssdw %ymm1, %ymm1, %ymm0 37 | 38 | # v16_int16 = {-4, -3, -2, 0, -4, -3, -2, 0, 0, 32767, -32768, 0, 0, 32767, -32768, 0}, 39 | # -> 40 | # v16_int16 = {-4, -3, -2, 0, 0, 32767, -32768, 0, -4, -3, -4, -3, -4, -3, -4, -3}, 41 | vpermd %ymm0, %ymm3, %ymm1 42 | 43 | # v8_int16 = {-4, -3, -2, 0, 0, 32767, -32768, 0}, 44 | vmovdqu %xmm1, (%rdx,%rcx,2) # store, 16-bit size 45 | 46 | add $8, %rcx # 8 samples at a time 47 | jmp loop 48 | 49 | remainder: 50 | cmp %rsi, %rcx 51 | jge done 52 | 53 | movss (%rdi,%rcx,4), %xmm0 54 | vcvtps2dq %xmm0, %xmm1 55 | vpackssdw %xmm1, %xmm1, %xmm0 56 | movq %xmm0, %rax 57 | mov %ax, (%rdx,%rcx,2) 58 | 59 | inc %rcx 60 | jmp remainder 61 | 62 | done: 63 | ret 64 | 65 | .data 66 | 67 | mask: 68 | .byte 0x00, 0x00, 0x00, 0x00 69 | .byte 0x01, 0x00, 0x00, 0x00 70 | .byte 0x04, 0x00, 0x00, 0x00 71 | .byte 0x05, 0x00, 0x00, 0x00 72 | .byte 0x00, 0x00, 0x00, 0x00 73 | .byte 0x00, 0x00, 0x00, 0x00 74 | .byte 0x00, 0x00, 0x00, 0x00 75 | .byte 0x00, 0x00, 0x00, 0x00 76 | 77 | csr: 78 | .byte 0x80, 0x1f, 0x00, 0x00 # [ IM DM ZM OM UM PM ] 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /lib/mvr2s_x64_avx512.S: -------------------------------------------------------------------------------- 1 | #if defined(__linux__) && defined(__ELF__) 2 | .section .note.GNU-stack,"",%progbits 3 | #endif 4 | 5 | #if defined(__x86_64__) 6 | 7 | .global mvr2s_avx512 8 | 9 | .text 10 | 11 | # void mvr2s_avx512(float *in, const uint16_t len, int16_t *out); 12 | # convert float array to int16 array with rounding and int16 saturation 13 | mvr2s_avx512: 14 | ldmxcsr csr(%rip) # set "round to nearest" 15 | 16 | mov %rsi, %rax 17 | and $-16, %al # 16 samples at a time 18 | 19 | xor %rcx, %rcx 20 | loop: 21 | cmp %rax, %rcx 22 | jge remainder 23 | 24 | vmovups (%rdi,%rcx,4), %zmm0 # load, 32-bit size 25 | 26 | # v16_float = {-2, -2.20000005, -1.70000005, -1.5, 0, 0, 2, 2.20000005, 1.70000005, 1.5, -19187.207, 15405.2158, -4437.91748, -18747.3066, -3701.35034, -19959.6738}, 27 | # -> 28 | # v16_int32 = {-2, -2, -2, -2, 0, 0, 2, 2, 2, 2, -19187, 15405, -4438, -18747, -3701, -19960}, 29 | vcvtps2dq %zmm0, %zmm1 30 | 31 | # v16_int32 = {-2, -2, -2, -2, 0, 0, 2, 2, 2, 2, -19187, 15405, -4438, -18747, -3701, -19960}, 32 | # -> 33 | # v16_int16 = {-2, -2, -2, -2, 0, 0, 2, 2, 2, 2, -19187, 15405, -4438, -18747, -3701, -19960}, 34 | vpmovsdw %zmm1, %ymm0 35 | 36 | vmovdqu %ymm0, (%rdx,%rcx,2) # store, 16-bit size 37 | 38 | add $16, %rcx # 16 samples at a time 39 | jmp loop 40 | 41 | remainder: 42 | cmp %rsi, %rcx 43 | jge done 44 | 45 | vmovss (%rdi,%rcx,4), %xmm0 46 | vcvtps2dq %ymm0, %ymm1 47 | vpmovsdw %ymm1, %xmm0 48 | vpextrw $0, %xmm0, (%rdx,%rcx,2) 49 | 50 | inc %rcx 51 | jmp remainder 52 | 53 | done: 54 | ret 55 | 56 | .data 57 | 58 | csr: 59 | .byte 0x80, 0x1f, 0x00, 0x00 # [ IM DM ZM OM UM PM ] 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /lib/poller.h: -------------------------------------------------------------------------------- 1 | #ifndef __POLLER_H__ 2 | #define __POLLER_H__ 3 | 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "compat.h" 12 | 13 | 14 | #define MAX_RTP_PACKET_SIZE 8192 15 | #define RTP_BUFFER_HEAD_ROOM 128 16 | #define RTP_BUFFER_TAIL_ROOM 512 17 | #define RTP_BUFFER_SIZE (MAX_RTP_PACKET_SIZE + RTP_BUFFER_HEAD_ROOM + RTP_BUFFER_TAIL_ROOM) 18 | 19 | 20 | struct obj; 21 | struct sockaddr; 22 | 23 | 24 | typedef void (*poller_func_t)(int, void *); 25 | 26 | struct poller_item { 27 | int fd; 28 | struct obj *obj; 29 | 30 | poller_func_t readable; 31 | void (*recv)(struct obj *, char *b, size_t len, struct sockaddr *, int64_t); 32 | poller_func_t writeable; 33 | poller_func_t closed; 34 | }; 35 | 36 | struct poller; 37 | 38 | struct poller *poller_new(void); 39 | void poller_free(struct poller **); 40 | bool poller_add_item(struct poller *, struct poller_item *); 41 | bool poller_del_item(struct poller *, int); 42 | bool poller_del_item_callback(struct poller *, int, void (*)(void *), void *); 43 | 44 | void poller_blocked(struct poller *, void *); 45 | bool poller_isblocked(struct poller *, void *); 46 | void poller_error(struct poller *, void *); 47 | 48 | void poller_loop(void *); 49 | 50 | extern bool (*rtpe_poller_add_item)(struct poller *, struct poller_item *); 51 | extern bool (*rtpe_poller_del_item)(struct poller *, int); 52 | extern bool (*rtpe_poller_del_item_callback)(struct poller *, int, void (*)(void *), void *); 53 | extern void (*rtpe_poller_blocked)(struct poller *, void *); 54 | extern bool (*rtpe_poller_isblocked)(struct poller *, void *); 55 | extern void (*rtpe_poller_error)(struct poller *, void *); 56 | 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /lib/resample.h: -------------------------------------------------------------------------------- 1 | #ifndef _RESAMPLE_H_ 2 | #define _RESAMPLE_H_ 3 | 4 | 5 | #include "codeclib.h" 6 | #include 7 | 8 | 9 | AVFrame *resample_frame(resample_t *resample, AVFrame *frame, const format_t *to_format); 10 | void resample_shutdown(resample_t *resample); 11 | 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /lib/rtcplib.h: -------------------------------------------------------------------------------- 1 | #ifndef _RTCPLIB_H_ 2 | #define _RTCPLIB_H_ 3 | 4 | #include 5 | #include "str.h" 6 | #include "compat.h" 7 | 8 | 9 | struct rtcp_header { 10 | #if G_BYTE_ORDER == G_BIG_ENDIAN 11 | unsigned version:2; /**< packet type */ 12 | unsigned p:1; /**< padding flag */ 13 | unsigned count:5; /**< varies by payload type */ 14 | #elif G_BYTE_ORDER == G_LITTLE_ENDIAN 15 | unsigned count:5; /**< varies by payload type */ 16 | unsigned p:1; /**< padding flag */ 17 | unsigned version:2; /**< packet type */ 18 | #else 19 | #error "byte order unknown" 20 | #endif 21 | unsigned char pt; 22 | uint16_t length; 23 | } __attribute__ ((packed)); 24 | 25 | struct rtcp_packet { 26 | struct rtcp_header header; 27 | uint32_t ssrc; 28 | } __attribute__ ((packed)); 29 | 30 | 31 | /* RFC 5761 section 4 */ 32 | INLINE bool rtcp_demux_is_rtcp(const str *s) { 33 | struct rtcp_packet *rtcp; 34 | 35 | if (s->len < sizeof(*rtcp)) 36 | return false; 37 | 38 | rtcp = (void *) s->s; 39 | 40 | if (rtcp->header.pt < 194) 41 | return false; 42 | if (rtcp->header.pt > 223) 43 | return false; 44 | return true; 45 | } 46 | 47 | 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /lib/spandsp_logging-01.h: -------------------------------------------------------------------------------- 1 | #define SPAN_LOG_ARGS int level, const char *text 2 | #define PHASE_E_HANDLER_ARGS t30_state_t *s, void *user_data, int result 3 | INLINE void my_span_set_log(logging_state_t *ls, message_handler_func_t h) { 4 | span_log_set_message_handler(ls, h); 5 | } 6 | INLINE void my_span_mh(message_handler_func_t h) { 7 | span_set_message_handler(h); 8 | } 9 | -------------------------------------------------------------------------------- /lib/spandsp_logging-02.h: -------------------------------------------------------------------------------- 1 | #define SPAN_LOG_ARGS void *user_data, int level, const char *text 2 | #define PHASE_E_HANDLER_ARGS void *user_data, int result 3 | INLINE void my_span_set_log(logging_state_t *ls, message_handler_func_t h) { 4 | span_log_set_message_handler(ls, h, NULL); 5 | } 6 | INLINE void my_span_mh(message_handler_func_t h) { 7 | span_set_message_handler(h, NULL); 8 | } 9 | -------------------------------------------------------------------------------- /lib/spandsp_logging-test.c: -------------------------------------------------------------------------------- 1 | #ifdef WITH_TRANSCODING 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "compat.h" 9 | #include "spandsp_logging.h" 10 | 11 | void logfunc(SPAN_LOG_ARGS) { 12 | return; 13 | } 14 | 15 | int main(void) { 16 | return 0; 17 | logging_state_t *ls = NULL; 18 | my_span_set_log(ls, logfunc); 19 | my_span_mh(NULL); 20 | return 0; 21 | } 22 | #else 23 | int main(void) { 24 | return 0; 25 | } 26 | #endif 27 | -------------------------------------------------------------------------------- /lib/ssllib.c: -------------------------------------------------------------------------------- 1 | #include "ssllib.h" 2 | #include 3 | #include 4 | #include "auxlib.h" 5 | #include "log.h" 6 | 7 | 8 | 9 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L 10 | EVP_MAC_CTX *rtpe_hmac_sha1_base; 11 | #endif 12 | 13 | 14 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 15 | static mutex_t *openssl_locks; 16 | 17 | static void cb_openssl_threadid(CRYPTO_THREADID *tid) { 18 | pthread_t me; 19 | 20 | me = pthread_self(); 21 | 22 | if (sizeof(me) == sizeof(void *)) 23 | CRYPTO_THREADID_set_pointer(tid, (void *) me); 24 | else 25 | CRYPTO_THREADID_set_numeric(tid, (unsigned long) me); 26 | } 27 | 28 | static void cb_openssl_lock(int mode, int type, const char *file, int line) { 29 | if ((mode & CRYPTO_LOCK)) 30 | mutex_lock(&openssl_locks[type]); 31 | else 32 | mutex_unlock(&openssl_locks[type]); 33 | } 34 | 35 | static void make_OpenSSL_thread_safe(void) { 36 | int i; 37 | 38 | openssl_locks = malloc(sizeof(*openssl_locks) * CRYPTO_num_locks()); 39 | for (i = 0; i < CRYPTO_num_locks(); i++) 40 | mutex_init(&openssl_locks[i]); 41 | 42 | CRYPTO_THREADID_set_callback(cb_openssl_threadid); 43 | CRYPTO_set_locking_callback(cb_openssl_lock); 44 | } 45 | #endif 46 | 47 | 48 | void rtpe_ssl_init(void) { 49 | ilog(LOG_INFO,"compile-time OpenSSL library: %s\n", OPENSSL_VERSION_TEXT); 50 | ilog(LOG_INFO,"run-time OpenSSL library: %s\n", OpenSSL_version(OPENSSL_VERSION)); 51 | 52 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) 53 | SSL_library_init(); 54 | SSL_load_error_strings(); 55 | make_OpenSSL_thread_safe(); 56 | #endif 57 | 58 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L 59 | if(EVP_default_properties_is_fips_enabled(NULL) == 1) { 60 | ilog(LOG_INFO,"FIPS mode enabled in OpenSSL library\n"); 61 | } else { 62 | ilog(LOG_DEBUG,"FIPS mode not enabled in OpenSSL library\n"); 63 | } 64 | 65 | EVP_MAC *rtpe_evp_hmac = EVP_MAC_fetch(NULL, "hmac", NULL); 66 | assert(rtpe_evp_hmac != NULL); 67 | 68 | rtpe_hmac_sha1_base = EVP_MAC_CTX_new(rtpe_evp_hmac); 69 | assert(rtpe_hmac_sha1_base != NULL); 70 | static const OSSL_PARAM params[2] = { 71 | OSSL_PARAM_utf8_string("digest", "sha-1", 5), 72 | OSSL_PARAM_END, 73 | }; 74 | EVP_MAC_CTX_set_params(rtpe_hmac_sha1_base, params); 75 | #endif 76 | } 77 | -------------------------------------------------------------------------------- /lib/ssllib.h: -------------------------------------------------------------------------------- 1 | #ifndef __SSLLIB_H__ 2 | #define __SSLLIB_H__ 3 | 4 | 5 | #include 6 | 7 | 8 | 9 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L 10 | extern EVP_MAC_CTX *rtpe_hmac_sha1_base; 11 | #endif 12 | 13 | 14 | 15 | void rtpe_ssl_init(void); 16 | 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /lib/streambuf.h: -------------------------------------------------------------------------------- 1 | #ifndef __BUFFER_H__ 2 | #define __BUFFER_H__ 3 | 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "compat.h" 12 | #include "str.h" 13 | #include "auxlib.h" 14 | 15 | 16 | 17 | struct poller; 18 | 19 | 20 | 21 | struct streambuf_funcs { 22 | ssize_t (*write)(void *, const void *, size_t); 23 | ssize_t (*read)(void *, void *, size_t); 24 | }; 25 | struct streambuf { 26 | mutex_t lock; 27 | GString *buf; 28 | void *fd_ptr; 29 | struct poller *poller; 30 | int64_t active_us; 31 | int eof; 32 | const struct streambuf_funcs 33 | *funcs; 34 | }; 35 | 36 | 37 | 38 | struct streambuf *streambuf_new(struct poller *, int); 39 | struct streambuf *streambuf_new_ptr(struct poller *, void *, const struct streambuf_funcs *); 40 | void streambuf_destroy(struct streambuf *); 41 | int streambuf_writeable(struct streambuf *); 42 | int streambuf_readable(struct streambuf *); 43 | char *streambuf_getline(struct streambuf *); 44 | size_t streambuf_bufsize(struct streambuf *); 45 | size_t streambuf_printf(struct streambuf *, const char *, ...) __attribute__ ((format (printf, 2, 3))); 46 | size_t streambuf_vprintf(struct streambuf *, const char *, va_list); 47 | void streambuf_write(struct streambuf *, const char *, unsigned int); 48 | INLINE void streambuf_write_str(struct streambuf *b, str *s) { 49 | streambuf_write(b, s->s, s->len); 50 | } 51 | 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /lib/uring.h: -------------------------------------------------------------------------------- 1 | #ifndef _URING_H_ 2 | #define _URING_H_ 3 | 4 | #include 5 | 6 | #include "socket.h" 7 | 8 | struct uring_req; 9 | 10 | typedef void uring_req_handler_fn(struct uring_req *, int32_t res, uint32_t flags); 11 | 12 | struct uring_req { 13 | uring_req_handler_fn *handler; 14 | }; 15 | 16 | struct uring_methods { 17 | ssize_t (*sendmsg)(socket_t *, struct msghdr *, const endpoint_t *, 18 | struct sockaddr_storage *, struct uring_req *); 19 | unsigned int (*thread_loop)(void); 20 | void (*free)(struct uring_req *); 21 | void *(*__alloc_req)(void *, size_t); 22 | }; 23 | 24 | extern __thread struct uring_methods uring_methods; 25 | 26 | INLINE void uring_req_free(struct uring_req *r, int32_t res, uint32_t flags) { 27 | uring_methods.free(r); 28 | } 29 | 30 | #define uring_alloc(sv, fn) ({ \ 31 | __typeof__(sv) __ret = uring_methods.__alloc_req((sv), sizeof(*(sv))); \ 32 | memset(sv, 0, sizeof(*(sv))); \ 33 | __ret->req.handler = (fn); \ 34 | __ret; \ 35 | }) 36 | 37 | 38 | #ifdef HAVE_LIBURING 39 | 40 | #include "bufferpool.h" 41 | 42 | void uring_thread_init(void); 43 | void uring_thread_cleanup(void); 44 | 45 | struct poller_item; 46 | struct poller *uring_poller_new(void); 47 | void uring_poller_free(struct poller **pp); 48 | void uring_poller_add_waker(struct poller *p); 49 | void uring_poller_wake(struct poller *p); 50 | void uring_poller_poll(struct poller *); 51 | void uring_poller_clear(struct poller *); 52 | 53 | bool uring_poller_add_item(struct poller *p, struct poller_item *i); 54 | bool uring_poller_del_item(struct poller *p, int fd); 55 | void uring_poller_blocked(struct poller *p, void *fdp); 56 | bool uring_poller_isblocked(struct poller *p, void *fdp); 57 | void uring_poller_error(struct poller *p, void *fdp); 58 | bool uring_poller_del_item_callback(struct poller *p, int fd, void (*callback)(void *), void *arg); 59 | 60 | #endif 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /perf-tester/.gitignore: -------------------------------------------------------------------------------- 1 | *.8 2 | *.o 3 | rtpengine-perftest 4 | core 5 | core.* 6 | .ycm_extra_conf.pyc 7 | auxlib.c 8 | loglib.c 9 | rtplib.c 10 | codeclib.c 11 | resample.c 12 | str.c 13 | fix_frame_channel_layout.h 14 | mvr2s_x64_avx512.S 15 | mvr2s_x64_avx2.S 16 | dtmflib.c 17 | poller.c 18 | ssllib.c 19 | bufferpool.c 20 | uring.c 21 | -------------------------------------------------------------------------------- /perf-tester/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = rtpengine-perftest 2 | 3 | export top_srcdir = .. 4 | 5 | include ../lib/deps.Makefile 6 | 7 | FIXTURES_PATH ?= ../fixtures 8 | 9 | ifeq ($(origin CFLAGS),undefined) 10 | CFLAGS ?= -g -Wall -Wextra -Wno-sign-compare -Wno-unused-parameter -Wstrict-prototypes -Werror=return-type \ 11 | -Wshadow 12 | else 13 | CFLAGS := $(CFLAGS) 14 | endif 15 | 16 | CFLAGS += -pthread 17 | CFLAGS += -std=c11 18 | CFLAGS += -I. -I../kernel-module/ -I../lib/ 19 | CFLAGS += -D_GNU_SOURCE 20 | CFLAGS += -DFIXTURES_PATH="\"$(FIXTURES_PATH)\"" 21 | CFLAGS += -DWITH_TRANSCODING 22 | CFLAGS += $(CFLAGS_LIBPCRE) 23 | CFLAGS += $(CFLAGS_GLIB) 24 | CFLAGS += $(CFLAGS_JSON_GLIB) 25 | CFLAGS += $(CFLAGS_GTHREAD) 26 | CFLAGS += $(CFLAGS_LIBAVCODEC) 27 | CFLAGS += $(CFLAGS_LIBAVFORMAT) 28 | CFLAGS += $(CFLAGS_LIBAVUTIL) 29 | CFLAGS += $(CFLAGS_LIBSWRESAMPLE) 30 | CFLAGS += $(CFLAGS_LIBAVFILTER) 31 | CFLAGS += $(CFLAGS_SPANDSP) 32 | CFLAGS += $(CFLAGS_OPUS) 33 | CFLAGS += $(CFLAGS_NCURSESW) 34 | CFLAGS += $(CFLAGS_OPENSSL) 35 | 36 | LDLIBS := -lm -ldl 37 | LDLIBS += $(LDLIBS_GLIB) 38 | LDLIBS += $(LDLIBS_JSON_GLIB) 39 | LDLIBS += $(LDLIBS_GTHREAD) 40 | LDLIBS += $(LDLIBS_LIBAVCODEC) 41 | LDLIBS += $(LDLIBS_LIBAVFORMAT) 42 | LDLIBS += $(LDLIBS_LIBAVUTIL) 43 | LDLIBS += $(LDLIBS_LIBSWRESAMPLE) 44 | LDLIBS += $(LDLIBS_LIBAVFILTER) 45 | LDLIBS += $(LDLIBS_SPANDSP) 46 | LDLIBS += $(LDLIBS_OPUS) 47 | LDLIBS += $(LDLIBS_NCURSESW) 48 | LDLIBS += $(LDLIBS_OPENSSL) 49 | 50 | CFLAGS += $(CFLAGS_BCG729) 51 | LDLIBS += $(LDLIBS_BCG729) 52 | 53 | CFLAGS += $(CFLAGS_CODEC_CHAIN) 54 | LDLIBS += $(LDLIBS_CODEC_CHAIN) 55 | 56 | SRCS = main.c log.c 57 | LIBSRCS = codeclib.strhash.c loglib.c auxlib.c resample.c str.c dtmflib.c rtplib.c poller.c ssllib.c bufferpool.c \ 58 | bencode.c uring.c 59 | LIBASM = mvr2s_x64_avx2.S mvr2s_x64_avx512.S 60 | 61 | OBJS = $(SRCS:.c=.o) $(LIBSRCS:.c=.o) $(LIBASM:.S=.o) 62 | 63 | include ../lib/common.Makefile 64 | 65 | main.o: fix_frame_channel_layout.h 66 | 67 | install: $(TARGET) 68 | install -m 0755 -D $(TARGET) $(DESTDIR)/usr/bin/$(TARGET) 69 | -------------------------------------------------------------------------------- /perf-tester/log.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | #include 3 | #include 4 | #include 5 | #include "loglib.h" 6 | 7 | 8 | static const uint max_log_lines = 10000; 9 | 10 | mutex_t log_lock = MUTEX_STATIC_INIT; 11 | static GQueue log_buffer = G_QUEUE_INIT; 12 | 13 | 14 | void __ilog(int prio, const char *fmt, ...) { 15 | va_list ap; 16 | 17 | GSList *to_free = NULL; 18 | 19 | va_start(ap, fmt); 20 | char *line = g_strdup_vprintf(fmt, ap); 21 | { 22 | LOCK(&log_lock); 23 | g_queue_push_tail(&log_buffer, line); 24 | while (log_buffer.length > max_log_lines) 25 | to_free = g_slist_prepend(to_free, g_queue_pop_head(&log_buffer)); 26 | } 27 | va_end(ap); 28 | 29 | g_slist_free_full(to_free, g_free); 30 | } 31 | 32 | 33 | GQueue *get_log_lines(uint num, uint end) { 34 | GQueue *ret = g_queue_new(); 35 | 36 | LOCK(&log_lock); 37 | 38 | GList *l = log_buffer.tail; 39 | while (l && end--) 40 | l = l->prev; 41 | for (; l && num; num--, l = l->prev) 42 | g_queue_push_head(ret, g_strdup(l->data)); 43 | 44 | return ret; 45 | } 46 | 47 | 48 | void log_clear(void) { 49 | LOCK(&log_lock); 50 | g_queue_clear_full(&log_buffer, g_free); 51 | } 52 | 53 | 54 | int get_local_log_level(unsigned int subsystem_idx) { 55 | return -1; 56 | } 57 | -------------------------------------------------------------------------------- /perf-tester/log.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOG_H__ 2 | #define __LOG_H__ 3 | 4 | #include "loglib.h" 5 | 6 | void __ilog(int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); 7 | 8 | GQueue *get_log_lines(uint num, uint end); 9 | 10 | void log_clear(void); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /perf-tester/log_funcs.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOG_FUNCS_H__ 2 | #define __LOG_FUNCS_H__ 3 | 4 | #include "compat.h" 5 | 6 | INLINE void log_info_reset(void) { } 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /perf-tester/loglevels.h: -------------------------------------------------------------------------------- 1 | ll(core, "Everything that isn't part of another subsystem") 2 | ll(spandsp, "Log messages generated by SpanDSP directly") 3 | ll(ffmpeg, "Log messages generated by ffmpeg directly") 4 | ll(transcoding, "Media and RTP transcoding") 5 | ll(codec, "Codec negotiation") 6 | ll(internals, "Noisy low-level internals") 7 | ll(dtx, "DTX timer/buffer") 8 | -------------------------------------------------------------------------------- /perl/NGCP/Rtpengine.pm: -------------------------------------------------------------------------------- 1 | package NGCP::Rtpengine; 2 | 3 | use strict; 4 | use warnings; 5 | use Socket; 6 | use Socket6; 7 | use IO::Socket; 8 | use IO::Socket::IP; 9 | use Bencode; 10 | use Data::Dumper; 11 | use JSON; 12 | use LWP::UserAgent; 13 | 14 | 15 | our $req_cb; 16 | 17 | 18 | sub new { 19 | my ($class, $addr, $port) = @_; 20 | 21 | my $self = {}; 22 | bless $self, $class; 23 | 24 | if (ref($addr)) { 25 | $self->{socket} = $addr; 26 | } 27 | elsif ($addr =~ /^http/) { 28 | $self->{uri} = $addr; 29 | } 30 | else { 31 | $self->{socket} = IO::Socket::IP->new(Type => &SOCK_DGRAM, Proto => 'udp', 32 | PeerHost => $addr, PeerPort => $port); 33 | } 34 | 35 | return $self; 36 | } 37 | 38 | sub req { 39 | my ($self, $packet) = @_; 40 | 41 | my $cookie = rand() . ' '; 42 | my $p = $cookie . ($self->{json} ? encode_json($packet) : Bencode::bencode($packet)); 43 | my $ret; 44 | if ($self->{uri}) { 45 | my $ua = LWP::UserAgent->new(); 46 | my $resp = $ua->post($self->{uri}, 47 | 'Content-type' => "application/x-rtpengine-ng", 48 | Content => $p); 49 | $ret = $resp->decoded_content; 50 | } 51 | else { 52 | $self->{socket}->send($p, 0) or die $!; 53 | if ($req_cb) { 54 | $req_cb->(); 55 | } 56 | $self->{socket}->recv($ret, 65535) or die $!; 57 | } 58 | $ret =~ s/^\Q$cookie\E//s or die $ret; 59 | my $resp = $self->{json} ? decode_json($ret) : Bencode::bdecode($ret, 1); 60 | 61 | $resp->{result} or die Dumper $resp; 62 | 63 | if ($resp->{result} eq 'error') { 64 | die "Error reason: \"$resp->{'error-reason'}\""; 65 | } 66 | 67 | return $resp; 68 | } 69 | 70 | sub offer { 71 | my ($self, $packet) = @_; 72 | return $self->req( { command => 'offer', %$packet } ); 73 | } 74 | sub answer { 75 | my ($self, $packet) = @_; 76 | return $self->req( { command => 'answer', %$packet } ); 77 | } 78 | 79 | 1; 80 | -------------------------------------------------------------------------------- /pkg/deb/backports/bookworm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Target dist: Debian Bookworm 4 | DIST=bookworm 5 | 6 | if [ ! -d ../../pkg/deb ] ; then 7 | echo "script needs to be executed at pkg/deb dir" >&2 8 | exit 1 9 | fi 10 | 11 | rm -rf ${DIST} 12 | cp -r debian ${DIST} 13 | 14 | if command -v wrap-and-sort &>/dev/null ; then 15 | wrap-and-sort -sat -d ${DIST} 16 | else 17 | echo "WARN: wrap-and-sort (Debian package devscripts) not available." 18 | fi 19 | 20 | # clean backports scripts 21 | rm -rf ${DIST}/backports 22 | exit 0 23 | -------------------------------------------------------------------------------- /pkg/deb/backports/noble: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Target dist: Ubuntu Noble 4 | DIST=noble 5 | 6 | if [ ! -d ../../pkg/deb ] ; then 7 | echo "script needs to be executed at pkg/deb dir" >&2 8 | exit 1 9 | fi 10 | 11 | rm -rf ${DIST} 12 | cp -r debian ${DIST} 13 | 14 | if command -v wrap-and-sort &>/dev/null ; then 15 | wrap-and-sort -sat -d ${DIST} 16 | else 17 | echo "WARN: wrap-and-sort (Debian package devscripts) not available." 18 | fi 19 | 20 | # clean backports scripts 21 | rm -rf ${DIST}/backports 22 | exit 0 23 | -------------------------------------------------------------------------------- /pkg/deb/backports/sid: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Target dist: Debian Sid 4 | DIST=sid 5 | 6 | if [ ! -d ../../pkg/deb ] ; then 7 | echo "script needs to be executed at pkg/deb dir" >&2 8 | exit 1 9 | fi 10 | 11 | rm -rf ${DIST} 12 | cp -r debian ${DIST} 13 | 14 | if command -v wrap-and-sort &>/dev/null ; then 15 | wrap-and-sort -sat -d ${DIST} 16 | else 17 | echo "WARN: wrap-and-sort (Debian package devscripts) not available." 18 | fi 19 | 20 | # clean backports scripts 21 | rm -rf ${DIST}/backports 22 | exit 0 23 | -------------------------------------------------------------------------------- /pkg/deb/backports/trixie: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Target dist: Debian Bookworm 4 | DIST=trixie 5 | 6 | if [ ! -d ../../pkg/deb ] ; then 7 | echo "script needs to be executed at pkg/deb dir" >&2 8 | exit 1 9 | fi 10 | 11 | rm -rf ${DIST} 12 | cp -r debian ${DIST} 13 | 14 | if command -v wrap-and-sort &>/dev/null ; then 15 | wrap-and-sort -sat -d ${DIST} 16 | else 17 | echo "WARN: wrap-and-sort (Debian package devscripts) not available." 18 | fi 19 | 20 | # clean backports scripts 21 | rm -rf ${DIST}/backports 22 | exit 0 23 | -------------------------------------------------------------------------------- /pkg/deb/generator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # produces non-NGCP pkg/deb/debian from debian 3 | 4 | if [ ! -d ../../pkg/deb ] ; then 5 | echo "script needs to be executed at pkg/deb dir" >&2 6 | exit 1 7 | fi 8 | 9 | rm -rf debian 10 | 11 | echo "- Copying origin debian dir" 12 | cp -ra ../../debian . 13 | echo "- Copying backports scripts" 14 | cp -ra backports debian 15 | 16 | # rules 17 | echo "- Remove ngcp- prefix" 18 | find debian -maxdepth 2 -type f -exec \ 19 | sed -i -e 's/ngcp-rtpengine/rtpengine/g' \ 20 | -e 's/ngcp\\-rtpengine/rtpengine/g' {} \; 21 | 22 | ## remove same file on links 23 | while read -r file; do 24 | file_new=$(echo "${file}" | sed -e 's/ngcp-//g') 25 | while read -r line; do 26 | sed -i -e "s#${line}\$#HH#g" "${file}" 27 | done < <(awk '{print $1}' "${file}") 28 | grep -v HH "${file}" > "${file_new}" 29 | rm "${file}" 30 | done < <(find debian -name '*links') 31 | 32 | echo "- Remove NGCP packages from control" 33 | sed -i -e '/ngcp-system-tools/d' debian/control 34 | sed -i -e '/ngcp-libcodec-chain/d' debian/control 35 | 36 | echo "- Set package-specific homepage" 37 | sed -i -e 's,^Homepage:.*,Homepage: https://rtpengine.com/,' debian/control 38 | 39 | echo "- Add Conflicts with NGCP packages" 40 | # "Package: rtpengine-daemon" already has a Conflicts field. Handle it here 41 | # separately, and exclude it from the batch rewrite below. 42 | sed -i '/^Conflicts/ a \ ngcp-rtpengine-daemon,' debian/control 43 | while read -r line ; do 44 | sed -i "/${line}$/ a Conflicts: ngcp-${line#Package: }" debian/control 45 | done < <(grep '^Package:' debian/control | grep -v ' rtpengine-daemon$') 46 | 47 | echo "- Rename files" 48 | while read -r file; do 49 | file_new=$(echo "${file}" | sed -e 's/ngcp-//g') 50 | mv "${file}" "${file_new}" 51 | done < <(find debian -maxdepth 1 -type f -name 'ngcp-rtpengine*') 52 | 53 | if ! command -v wrap-and-sort &>/dev/null ; then 54 | echo "WARN: wrap-and-sort (Debian package devscripts) not available." 55 | else 56 | echo "- Remove empty Suggests" 57 | wrap-and-sort 58 | sed -i -e '/Suggests:$/d' debian/control 59 | wrap-and-sort -sat 60 | fi 61 | -------------------------------------------------------------------------------- /pkg/deb/old-dkms/dkms.conf.in: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME="rtpengine" 2 | PACKAGE_VERSION="__VERSION__" 3 | MAKE[0]="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build RTPENGINE_VERSION=\"__VERSION__\"" 4 | CLEAN="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean" 5 | AUTOINSTALL=yes 6 | BUILT_MODULE_NAME[0]="xt_RTPENGINE" 7 | DEST_MODULE_LOCATION[0]=/extra 8 | -------------------------------------------------------------------------------- /pkg/deb/old-dkms/rtpengine-kernel-dkms.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | package=rtpengine-kernel-dkms 6 | name=rtpengine 7 | 8 | version=$(dpkg-query -W -f="\${Version}" "$package" \ 9 | |rev|cut -d- -f2-|rev|cut -d':' -f2|tr -d "\n") 10 | 11 | 12 | if [ -x "$(which ngcp-virt-identify)" ]; then 13 | if ngcp-virt-identify --type container; then 14 | VIRT="yes" 15 | fi 16 | fi 17 | 18 | if [ "$VIRT" = "yes" ]; then 19 | echo "Container environment detected. Skip dkms" 20 | else 21 | isadded=$(dkms status -m "$name" -v "$version") 22 | if [ -z "${isadded}" ] ; then 23 | dkms add -m "$name" -v "$version" 24 | fi 25 | 26 | if [ "$1" = 'configure' ] ; then 27 | KERNELS=$(ls /lib/modules/ 2>/dev/null || true) 28 | for kernel in $KERNELS; do 29 | if [ ! -r "/lib/modules/$kernel/build" ]; then 30 | # cannot build modules for this kernel 31 | continue 32 | fi 33 | ( dkms build -m "$name" -v "$version" -k "$kernel" && dkms install -m "$name" -v "$version" -k "$kernel" ) || true 34 | done 35 | 36 | # try to start the daemon 37 | if [ -x /etc/init.d/rtpengine-daemon ] ; then 38 | invoke-rc.d rtpengine-daemon restart || true 39 | fi 40 | fi 41 | fi 42 | 43 | #DEBHELPER# 44 | 45 | exit 0 46 | 47 | -------------------------------------------------------------------------------- /pkg/deb/old-dkms/rtpengine-kernel-dkms.prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | package=rtpengine-kernel-dkms 6 | name=rtpengine 7 | modname=xt_RTPENGINE 8 | 9 | version=$(dpkg-query -W -f="\${Version}" "$package" \ 10 | |rev|cut -d- -f2-|rev|cut -d':' -f2|tr -d "\n") 11 | 12 | if [ -x "$(which ngcp-virt-identify)" ]; then 13 | if ngcp-virt-identify --type container; then 14 | VIRT="yes" 15 | fi 16 | fi 17 | 18 | # make sure it's not running 19 | if [ -x /etc/init.d/rtpengine-daemon ] ; then 20 | invoke-rc.d rtpengine-daemon stop || true 21 | rmmod "$modname" 2>/dev/null || true 22 | fi 23 | 24 | if [ "$VIRT" = "yes" ]; then 25 | echo "Container environment detected. Skip dkms" 26 | else 27 | dkms remove -m "$name" -v "$version" --all || true 28 | fi 29 | 30 | #DEBHELPER# 31 | 32 | exit 0 33 | -------------------------------------------------------------------------------- /recording-daemon/.gitignore: -------------------------------------------------------------------------------- 1 | .depend 2 | *.o 3 | core 4 | core.* 5 | .ycm_extra_conf.pyc 6 | rtpengine-recording 7 | auxlib.c 8 | loglib.c 9 | rtplib.c 10 | codeclib.c 11 | resample.c 12 | str.c 13 | fix_frame_channel_layout.h 14 | socket.c 15 | streambuf.c 16 | ssllib.c 17 | dtmflib.c 18 | *-test 19 | *-test.c 20 | *.8 21 | mvr2s_x64_avx512.S 22 | mvr2s_x64_avx2.S 23 | mix_in_x64_avx2.S 24 | mix_in_x64_avx512bw.S 25 | mix_in_x64_sse2.S 26 | bufferpool.c 27 | uring.c 28 | -------------------------------------------------------------------------------- /recording-daemon/Makefile: -------------------------------------------------------------------------------- 1 | TARGET= rtpengine-recording 2 | 3 | export top_srcdir = .. 4 | 5 | include ../lib/deps.Makefile 6 | 7 | ifeq ($(origin CFLAGS),undefined) 8 | CFLAGS?= -g -Wall -Wextra -Wno-sign-compare -Wno-unused-parameter -Wstrict-prototypes -Werror=return-type \ 9 | -Wshadow 10 | else 11 | CFLAGS:= $(CFLAGS) 12 | endif 13 | CFLAGS+= -pthread 14 | CFLAGS+= -std=c11 15 | CFLAGS+= -I. -I../lib/ -I../kernel-module/ 16 | CFLAGS+= -D_GNU_SOURCE -D_POSIX_SOURCE -D_POSIX_C_SOURCE 17 | CFLAGS+= $(CFLAGS_LIBPCRE) 18 | CFLAGS+= $(CFLAGS_GLIB) 19 | CFLAGS+= $(CFLAGS_JSON_GLIB) 20 | CFLAGS+= $(CFLAGS_GTHREAD) 21 | CFLAGS+= $(CFLAGS_LIBAVCODEC) 22 | CFLAGS+= $(CFLAGS_LIBAVFORMAT) 23 | CFLAGS+= $(CFLAGS_LIBAVUTIL) 24 | CFLAGS+= $(CFLAGS_LIBSWRESAMPLE) 25 | CFLAGS+= $(CFLAGS_LIBAVFILTER) 26 | CFLAGS+= $(CFLAGS_OPUS) 27 | CFLAGS+= $(CFLAGS_MYSQL) 28 | CFLAGS+= $(CFLAGS_OPENSSL) 29 | CFLAGS+= $(CFLAGS_LIBCURL) 30 | 31 | LDLIBS:= -lm -ldl 32 | LDLIBS+= $(LDLIBS_GLIB) 33 | LDLIBS+= $(LDLIBS_JSON_GLIB) 34 | LDLIBS+= $(LDLIBS_GTHREAD) 35 | LDLIBS+= $(LDLIBS_LIBAVCODEC) 36 | LDLIBS+= $(LDLIBS_LIBAVFORMAT) 37 | LDLIBS+= $(LDLIBS_LIBAVUTIL) 38 | LDLIBS+= $(LDLIBS_LIBSWRESAMPLE) 39 | LDLIBS+= $(LDLIBS_LIBAVFILTER) 40 | LDLIBS+= $(LDLIBS_OPUS) 41 | LDLIBS+= $(LDLIBS_MYSQL) 42 | LDLIBS+= $(LDLIBS_OPENSSL) 43 | LDLIBS+= $(LDLIBS_LIBCURL) 44 | 45 | CFLAGS+= $(CFLAGS_BCG729) 46 | LDLIBS+= $(LDLIBS_BCG729) 47 | 48 | SRCS= epoll.c garbage.c inotify.c main.c metafile.c stream.c recaux.c packet.c \ 49 | decoder.c output.c mix.c db.c log.c forward.c tag.c poller.c notify.c 50 | LIBSRCS= loglib.c auxlib.c rtplib.c codeclib.strhash.c resample.c str.c socket.c streambuf.c ssllib.c \ 51 | dtmflib.c bufferpool.c bencode.c 52 | LIBASM= mvr2s_x64_avx2.S mvr2s_x64_avx512.S mix_in_x64_avx2.S mix_in_x64_avx512bw.S mix_in_x64_sse2.S 53 | OBJS= $(SRCS:.c=.o) $(LIBSRCS:.c=.o) $(LIBASM:.S=.o) 54 | 55 | MDS= rtpengine-recording.ronn 56 | MANS= $(MDS:.ronn=.8) 57 | 58 | include ../lib/common.Makefile 59 | 60 | install: $(TARGET) $(MANS) 61 | install -m 0755 -D $(TARGET) $(DESTDIR)/usr/bin/$(TARGET) 62 | install -m 0644 -D $(TARGET).8 $(DESTDIR)/usr/share/man/man8/$(TARGET).8 63 | -------------------------------------------------------------------------------- /recording-daemon/db.h: -------------------------------------------------------------------------------- 1 | #ifndef _DB_H_ 2 | #define _DB_H_ 3 | 4 | #include "types.h" 5 | 6 | 7 | void db_do_call(metafile_t *); 8 | void db_close_call(metafile_t *); 9 | void db_do_stream(metafile_t *mf, output_t *op, stream_t *, unsigned long ssrc); 10 | void db_close_stream(output_t *op, FILE *, GString *); 11 | void db_delete_stream(metafile_t *, output_t *op); 12 | void db_config_stream(output_t *op); 13 | void db_thread_end(void); 14 | 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /recording-daemon/decoder.h: -------------------------------------------------------------------------------- 1 | #ifndef _DECODER_H_ 2 | #define _DECODER_H_ 3 | 4 | #include "types.h" 5 | #include "str.h" 6 | 7 | 8 | extern int resample_audio; 9 | 10 | 11 | decode_t *decoder_new(const char *payload_str, const char *format, int ptime, output_t *); 12 | int decoder_input(decode_t *, const str *, unsigned long ts, ssrc_t *); 13 | void decoder_free(decode_t *); 14 | 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /recording-daemon/epoll.c: -------------------------------------------------------------------------------- 1 | #include "epoll.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "log.h" 8 | #include "main.h" 9 | #include "garbage.h" 10 | #include "db.h" 11 | 12 | 13 | static int epoll_fd = -1; 14 | 15 | 16 | void epoll_setup(void) { 17 | epoll_fd = epoll_create1(0); 18 | if (epoll_fd == -1) 19 | die_errno("epoll_create1 failed"); 20 | 21 | } 22 | 23 | 24 | int epoll_add(int fd, uint32_t events, handler_t *handler) { 25 | struct epoll_event epev = { .events = events | EPOLLET, .data = { .ptr = handler } }; 26 | int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &epev); 27 | return ret; 28 | } 29 | 30 | 31 | void epoll_del(int fd) { 32 | epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL); 33 | } 34 | 35 | 36 | static void poller_thread_end(void *ptr) { 37 | mysql_thread_end(); 38 | db_thread_end(); 39 | } 40 | 41 | 42 | void *poller_thread(void *ptr) { 43 | struct epoll_event epev; 44 | unsigned int me_num = GPOINTER_TO_UINT(ptr); 45 | 46 | dbg("poller thread %u running", me_num); 47 | 48 | mysql_thread_init(); 49 | 50 | thread_cleanup_push(poller_thread_end, NULL); 51 | 52 | while (!shutdown_flag) { 53 | pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 54 | int ret = epoll_wait(epoll_fd, &epev, 1, 10000); 55 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 56 | 57 | if (ret == -1) { 58 | if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) 59 | continue; 60 | die_errno("epoll_wait failed"); 61 | } 62 | 63 | if (ret > 0) { 64 | dbg("thread %u handling event", me_num); 65 | 66 | handler_t *handler = epev.data.ptr; 67 | handler->func(handler); 68 | } 69 | 70 | garbage_collect(me_num); 71 | } 72 | 73 | thread_cleanup_pop(true); 74 | 75 | return NULL; 76 | } 77 | 78 | 79 | void epoll_cleanup(void) { 80 | close(epoll_fd); 81 | } 82 | -------------------------------------------------------------------------------- /recording-daemon/epoll.h: -------------------------------------------------------------------------------- 1 | #ifndef _EPOLL_H_ 2 | #define _EPOLL_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "types.h" 8 | 9 | 10 | void epoll_setup(void); 11 | void epoll_cleanup(void); 12 | 13 | int epoll_add(int fd, uint32_t events, handler_t *handler); 14 | void epoll_del(int fd); 15 | 16 | 17 | void *poller_thread(void *ptr); 18 | 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /recording-daemon/forward.c: -------------------------------------------------------------------------------- 1 | #include "forward.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "main.h" 7 | #include "log.h" 8 | 9 | void start_forwarding_capture(metafile_t *mf, char *meta_info) { 10 | int sock; 11 | struct sockaddr_un addr; 12 | 13 | if (mf->forward_fd >= 0) { 14 | ilog(LOG_INFO, "Connection already established"); 15 | return; 16 | } 17 | 18 | #ifdef SOCK_SEQPACKET 19 | if ((sock = socket(AF_UNIX, SOCK_SEQPACKET, 0)) == -1) { 20 | #else 21 | if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) { 22 | #endif 23 | ilog(LOG_ERR, "Error creating socket: %s", strerror(errno)); 24 | return; 25 | } 26 | 27 | memset(&addr, 0, sizeof(addr)); 28 | addr.sun_family = AF_UNIX; 29 | strncpy(addr.sun_path, forward_to, sizeof(addr.sun_path) - 1); 30 | 31 | if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { 32 | ilog(LOG_ERR, "Error setting socket non-blocking: %s", strerror(errno)); 33 | goto err; 34 | } 35 | 36 | if (connect(sock, (struct sockaddr*) &addr, sizeof(addr)) == -1) { 37 | ilog(LOG_ERR, "Error connecting to socket %s : %s", addr.sun_path, 38 | strerror(errno)); 39 | goto err; 40 | } 41 | 42 | if (send(sock, meta_info, strlen(meta_info), 0) == -1) { 43 | ilog(LOG_ERR, "Error sending meta info: %s. Call will not be forwarded", strerror(errno)); 44 | goto err; 45 | } 46 | 47 | mf->forward_fd = sock; 48 | return; 49 | err: 50 | close(sock); 51 | } 52 | 53 | int forward_packet(metafile_t *mf, unsigned char *buf, unsigned len) { 54 | 55 | if (mf->forward_fd == -1) { 56 | ilog(LOG_ERR, 57 | "Trying to send packets, but connection not initialized!"); 58 | goto err; 59 | } 60 | 61 | if (send(mf->forward_fd, buf, len, 0) == -1) { 62 | if (errno == EAGAIN || errno == EWOULDBLOCK) 63 | ilog(LOG_DEBUG, "Dropping packet since call would block"); 64 | else 65 | ilog(LOG_ERR, "Error sending: %s", strerror(errno)); 66 | goto err; 67 | } 68 | 69 | return 0; 70 | 71 | err: 72 | return -1; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /recording-daemon/forward.h: -------------------------------------------------------------------------------- 1 | #ifndef _FORWARD_H_ 2 | #define _FORWARD_H_ 3 | 4 | #include "types.h" 5 | 6 | void start_forwarding_capture(metafile_t *mf, char *meta_info); 7 | int forward_packet(metafile_t *mf, unsigned char *buf, unsigned len); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /recording-daemon/garbage.h: -------------------------------------------------------------------------------- 1 | #ifndef _GARBAGE_H_ 2 | #define _GARBAGE_H_ 3 | 4 | typedef void free_func_t(void *); 5 | 6 | unsigned int garbage_new_thread_num(void); 7 | void garbage_add(void *ptr, free_func_t *free_func); 8 | void garbage_collect(unsigned int num); 9 | void garbage_collect_all(void); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /recording-daemon/inotify.c: -------------------------------------------------------------------------------- 1 | #include "inotify.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "log.h" 8 | #include "main.h" 9 | #include "epoll.h" 10 | #include "metafile.h" 11 | 12 | 13 | static int inotify_fd = -1; 14 | 15 | 16 | static handler_func inotify_handler_func; 17 | static handler_t inotify_handler = { 18 | .func = inotify_handler_func, 19 | }; 20 | 21 | 22 | static void inotify_close_write(struct inotify_event *inev) { 23 | dbg("inotify close_write(%s%s%s)", FMT_M(inev->name)); 24 | metafile_change(inev->name); 25 | } 26 | 27 | 28 | static void inotify_delete(struct inotify_event *inev) { 29 | dbg("inotify delete(%s%s%s)", FMT_M(inev->name)); 30 | metafile_delete(inev->name); 31 | } 32 | 33 | 34 | static void inotify_handler_func(handler_t *handler) { 35 | char buf[4 * (sizeof(struct inotify_event) + NAME_MAX + 1)]; 36 | 37 | while (1) { 38 | int ret = read(inotify_fd, buf, sizeof(buf)); 39 | if (ret == -1) { 40 | if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) 41 | break; 42 | die_errno("read on inotify fd failed"); 43 | } 44 | if (ret == 0) 45 | die("EOF on inotify fd"); 46 | 47 | char *bufend = buf + ret; 48 | char *bufhead = buf; 49 | while (bufhead < bufend) { 50 | struct inotify_event *inev = (void *) bufhead; 51 | 52 | if ((inev->mask & IN_DELETE)) 53 | inotify_delete(inev); 54 | if ((inev->mask & IN_CLOSE_WRITE)) 55 | inotify_close_write(inev); 56 | 57 | bufhead += sizeof(*inev) + inev->len; 58 | } 59 | } 60 | } 61 | 62 | 63 | void inotify_setup(void) { 64 | inotify_fd = inotify_init1(IN_NONBLOCK); 65 | if (inotify_fd == -1) 66 | die_errno("inotify_init1 failed"); 67 | 68 | int ret = inotify_add_watch(inotify_fd, spool_dir, IN_CLOSE_WRITE | IN_DELETE); 69 | if (ret == -1) 70 | die_errno("inotify_add_watch failed"); 71 | 72 | if (epoll_add(inotify_fd, EPOLLIN, &inotify_handler)) 73 | die_errno("failed to add inotify_fd to epoll"); 74 | } 75 | 76 | 77 | void inotify_cleanup(void) { 78 | close(inotify_fd); 79 | } 80 | -------------------------------------------------------------------------------- /recording-daemon/inotify.h: -------------------------------------------------------------------------------- 1 | #ifndef _INOTIFY_H_ 2 | #define _INOTIFY_H_ 3 | 4 | void inotify_setup(void); 5 | void inotify_cleanup(void); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /recording-daemon/log.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | #include 3 | #include 4 | #include 5 | #include "loglib.h" 6 | 7 | 8 | __thread const char *log_info_call, *log_info_stream; 9 | __thread unsigned long log_info_ssrc; 10 | 11 | 12 | void __ilog(int prio, const char *fmt, ...) { 13 | va_list ap; 14 | char prefix[300] = ""; 15 | char *pp = prefix; 16 | char *endp = prefix + sizeof(prefix); 17 | 18 | if (log_info_call) 19 | pp += snprintf(pp, endp - pp, "[C %s%s%s] ", FMT_M(log_info_call)); 20 | if (log_info_stream) 21 | pp += snprintf(pp, endp - pp, "[S %s%s%s] ", FMT_M(log_info_stream)); 22 | if (log_info_ssrc) 23 | pp += snprintf(pp, endp - pp, "[%s0x%lx%s] ", FMT_M(log_info_ssrc)); 24 | 25 | va_start(ap, fmt); 26 | __vpilog(prio, prefix, fmt, ap); 27 | va_end(ap); 28 | } 29 | 30 | int get_local_log_level(unsigned int subsystem_idx) { 31 | return -1; 32 | } 33 | -------------------------------------------------------------------------------- /recording-daemon/log.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOG_H_ 2 | #define _LOG_H_ 3 | 4 | #include "loglib.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define dbg(fmt, ...) ilog(LOG_DEBUG, "[%s:%i] " fmt, __FILE__, __LINE__, ##__VA_ARGS__) 12 | #define __C_DBG(x...) ilog(LOG_DEBUG, x) 13 | 14 | void __ilog(int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); 15 | 16 | extern __thread const char *log_info_call, *log_info_stream; 17 | extern __thread unsigned long log_info_ssrc; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /recording-daemon/loglevels.h: -------------------------------------------------------------------------------- 1 | ll(core, "Everything that isn't part of another subsystem") 2 | ll(ffmpeg, "Log messages generated by ffmpeg directly") 3 | ll(internals, "Noisy low-level internals") 4 | -------------------------------------------------------------------------------- /recording-daemon/main.h: -------------------------------------------------------------------------------- 1 | #ifndef _MAIN_H_ 2 | #define _MAIN_H_ 3 | 4 | 5 | #include "auxlib.h" 6 | #include "socket.h" 7 | #include 8 | 9 | 10 | enum output_storage_enum { 11 | OUTPUT_STORAGE_FILE = 0x1, 12 | OUTPUT_STORAGE_DB = 0x2, 13 | OUTPUT_STORAGE_BOTH = 0x3, 14 | OUTPUT_STORAGE_DB_MEMORY = 0x4, 15 | }; 16 | enum mix_method { 17 | MM_DIRECT = 0, 18 | MM_CHANNELS, 19 | }; 20 | 21 | extern int ktable; 22 | extern int num_threads; 23 | extern enum output_storage_enum output_storage; 24 | extern char *spool_dir; 25 | extern char *output_dir; 26 | extern gboolean output_mixed; 27 | extern enum mix_method mix_method; 28 | extern int mix_num_inputs; 29 | extern gboolean output_single; 30 | extern gboolean output_enabled; 31 | extern mode_t output_chmod; 32 | extern mode_t output_chmod_dir; 33 | extern uid_t output_chown; 34 | extern gid_t output_chgrp; 35 | extern char *output_pattern; 36 | extern int output_buffer; 37 | extern gboolean decoding_enabled; 38 | extern char *c_mysql_host, 39 | *c_mysql_user, 40 | *c_mysql_pass, 41 | *c_mysql_db; 42 | extern int c_mysql_port; 43 | extern char *forward_to; 44 | extern endpoint_t tls_send_to_ep; 45 | extern int tls_resample; 46 | extern bool tls_disable; 47 | extern char *notify_uri; 48 | extern gboolean notify_post; 49 | extern gboolean notify_nverify; 50 | extern int notify_threads; 51 | extern int notify_retries; 52 | extern char *notify_command; 53 | extern gboolean notify_record; 54 | extern gboolean notify_purge; 55 | extern gboolean mix_output_per_media; 56 | extern volatile int shutdown_flag; 57 | extern gboolean flush_packets; 58 | 59 | extern struct rtpengine_common_config rtpe_common_config; 60 | 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /recording-daemon/metafile.h: -------------------------------------------------------------------------------- 1 | #ifndef _METAFILE_H_ 2 | #define _METAFILE_H_ 3 | 4 | #include "types.h" 5 | 6 | void metafile_setup(void); 7 | void metafile_cleanup(void); 8 | 9 | void metafile_change(char *name); 10 | void metafile_delete(char *name); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /recording-daemon/mix.h: -------------------------------------------------------------------------------- 1 | #ifndef _MIX_H_ 2 | #define _MIX_H_ 3 | 4 | #include "types.h" 5 | #include 6 | 7 | #define MIX_MAX_INPUTS 4 8 | 9 | mix_t *mix_new(void); 10 | void mix_destroy(mix_t *mix); 11 | void mix_set_channel_slots(mix_t *mix, unsigned int); 12 | int mix_config(mix_t *, const format_t *format); 13 | int mix_add(mix_t *mix, AVFrame *frame, unsigned int idx, void *, output_t *output); 14 | unsigned int mix_get_index(mix_t *, void *, unsigned int, unsigned int); 15 | #endif 16 | 17 | -------------------------------------------------------------------------------- /recording-daemon/notify.h: -------------------------------------------------------------------------------- 1 | #ifndef _NOTIFY_H_ 2 | #define _NOTIFY_H_ 3 | 4 | #include 5 | #include "types.h" 6 | 7 | void notify_setup(void); 8 | void notify_cleanup(void); 9 | 10 | void notify_push_output(output_t *, metafile_t *, tag_t *); 11 | void notify_push_call(metafile_t *); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /recording-daemon/output.h: -------------------------------------------------------------------------------- 1 | #ifndef _OUTPUT_H_ 2 | #define _OUTPUT_H_ 3 | 4 | #include "types.h" 5 | #include 6 | 7 | 8 | extern int mp3_bitrate; 9 | 10 | 11 | void output_init(const char *format); 12 | 13 | output_t *output_new_ext(metafile_t *, const char *type, const char *kind, const char *label); 14 | void output_close(metafile_t *, output_t *, tag_t *, bool discard); 15 | 16 | int output_config(output_t *output, const format_t *requested_format, format_t *actual_format); 17 | int output_add(output_t *output, AVFrame *frame); 18 | 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /recording-daemon/packet.h: -------------------------------------------------------------------------------- 1 | #ifndef _PACKET_H_ 2 | #define _PACKET_H_ 3 | 4 | #include "types.h" 5 | #include 6 | 7 | void ssrc_close(ssrc_t *s); 8 | void ssrc_free(void *p); 9 | 10 | void packet_process(stream_t *, unsigned char *, unsigned len); 11 | 12 | void ssrc_tls_state(ssrc_t *ssrc); 13 | void ssrc_tls_fwd_silence_frames_upto(ssrc_t *ssrc, AVFrame *frame, int64_t upto); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /recording-daemon/poller.c: -------------------------------------------------------------------------------- 1 | #include "poller.h" 2 | 3 | void poller_blocked(struct poller *p, void *fdp) { 4 | p->state = PS_WRITE_BLOCKED; 5 | } 6 | bool poller_isblocked(struct poller *p, void *fdp) { 7 | return p->state != PS_OPEN; 8 | } 9 | void poller_error(struct poller *p, void *fdp) { 10 | p->state = PS_ERROR; 11 | } 12 | -------------------------------------------------------------------------------- /recording-daemon/poller.h: -------------------------------------------------------------------------------- 1 | #ifndef __POLLER_H__ 2 | #define __POLLER_H__ 3 | 4 | #include 5 | 6 | // dummy poller 7 | struct poller { 8 | enum { 9 | PS_CLOSED = 0, 10 | PS_CONNECTING, 11 | PS_HANDSHAKE, 12 | PS_OPEN, 13 | PS_WRITE_BLOCKED, 14 | PS_ERROR, 15 | } state; 16 | }; 17 | 18 | void poller_blocked(struct poller *, void *); 19 | void poller_error(struct poller *, void *); 20 | bool poller_isblocked(struct poller *, void *); 21 | 22 | #define rtpe_poller_isblocked poller_isblocked 23 | #define rtpe_poller_blocked poller_blocked 24 | #define rtpe_poller_error poller_error 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /recording-daemon/recaux.c: -------------------------------------------------------------------------------- 1 | #include "recaux.h" 2 | #include 3 | #include 4 | 5 | 6 | __thread int __sscanf_hack_var; 7 | 8 | 9 | int __sscanf_match(const char *s, const char *fmt, ...) { 10 | va_list ap; 11 | 12 | __sscanf_hack_var = 0; // to make sure that sscanf consumes the entire string 13 | 14 | va_start(ap, fmt); 15 | int ret = vsscanf(s, fmt, ap); 16 | va_end(ap); 17 | 18 | if (__sscanf_hack_var == 0) 19 | return 0; 20 | return ret; 21 | } 22 | -------------------------------------------------------------------------------- /recording-daemon/recaux.h: -------------------------------------------------------------------------------- 1 | #ifndef _RECAUX_H_ 2 | #define _RECAUX_H_ 3 | 4 | extern __thread int __sscanf_hack_var; 5 | 6 | #define sscanf_match(str, format, ...) __sscanf_match(str, format "%n", ##__VA_ARGS__, &__sscanf_hack_var) 7 | int __sscanf_match(const char *str, const char *fmt, ...) __attribute__ ((__format__ (__scanf__, 2, 3))); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /recording-daemon/stream.h: -------------------------------------------------------------------------------- 1 | #ifndef _STREAM_H_ 2 | #define _STREAM_H_ 3 | 4 | #include "types.h" 5 | 6 | void stream_open(metafile_t *mf, unsigned long id, char *name); 7 | void stream_details(metafile_t *mf, unsigned long id, unsigned int tag, unsigned int media_sdp_id, unsigned int channel_slot); 8 | void stream_forwarding_on(metafile_t *mf, unsigned long id, unsigned int on); 9 | void stream_sdp_label(metafile_t *mf, unsigned long id, unsigned long *label); 10 | void stream_close(stream_t *stream); 11 | void stream_free(stream_t *stream); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /recording-daemon/tag.c: -------------------------------------------------------------------------------- 1 | #include "tag.h" 2 | 3 | 4 | // XXX copied from stream.c - unify? 5 | tag_t *tag_get(metafile_t *mf, unsigned long id) { 6 | if (mf->tags->len <= id) 7 | g_ptr_array_set_size(mf->tags, id + 1); 8 | tag_t *ret = g_ptr_array_index(mf->tags, id); 9 | if (ret) 10 | goto out; 11 | 12 | ret = g_new0(tag_t, 1); 13 | g_ptr_array_index(mf->tags, id) = ret; 14 | 15 | ret->id = id; 16 | 17 | out: 18 | return ret; 19 | } 20 | 21 | void tag_name(metafile_t *mf, unsigned long t, const char *s) { 22 | tag_t *tag = tag_get(mf, t); 23 | tag->name = g_string_chunk_insert(mf->gsc, s); 24 | } 25 | 26 | void tag_label(metafile_t *mf, unsigned long t, const char *s) { 27 | tag_t *tag = tag_get(mf, t); 28 | tag->label = g_string_chunk_insert(mf->gsc, s); 29 | } 30 | 31 | void tag_metadata(metafile_t *mf, unsigned long t, const char *s) { 32 | tag_t *tag = tag_get(mf, t); 33 | tag->metadata = g_string_chunk_insert(mf->gsc, s); 34 | } 35 | 36 | void tag_free(tag_t *tag) { 37 | g_free(tag); 38 | } 39 | -------------------------------------------------------------------------------- /recording-daemon/tag.h: -------------------------------------------------------------------------------- 1 | #ifndef _TAG_H_ 2 | #define _TAG_H_ 3 | 4 | #include "types.h" 5 | 6 | tag_t *tag_get(metafile_t *mf, unsigned long id); 7 | void tag_name(metafile_t *mf, unsigned long t, const char *); 8 | void tag_label(metafile_t *mf, unsigned long t, const char *); 9 | void tag_metadata(metafile_t *mf, unsigned long t, const char *); 10 | void tag_free(tag_t *); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /t/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | bitstr-test 3 | amr-encode-test 4 | amr-decode-test 5 | core 6 | .depend 7 | auxlib.c 8 | codeclib.c 9 | fix_frame_channel_layout.h 10 | loglib.c 11 | resample.c 12 | rtplib.c 13 | str.c 14 | crypto.c 15 | aes-crypt 16 | bencode.c 17 | call.c 18 | call_interfaces.c 19 | cdr.c 20 | codec.c 21 | control_ng.c 22 | control_ng_flags_parser.c 23 | cookie_cache.c 24 | dtls.c 25 | graphite.c 26 | helpers.c 27 | homer.c 28 | ice.c 29 | iptables.c 30 | kernel.c 31 | load.c 32 | media_socket.c 33 | poller.c 34 | recording.c 35 | redis.c 36 | rtcp.c 37 | rtp.c 38 | sdp.c 39 | socket.c 40 | ssrc.c 41 | statistics.c 42 | streambuf.c 43 | stun.c 44 | transcode-test 45 | udp_listener.c 46 | payload-tracker-test 47 | dtmf.c 48 | const_str_hash-test.strhash 49 | tests-preload.so 50 | timerthread.c 51 | media_player.c 52 | dtmflib.c 53 | test-dtmf-detect 54 | test-bitstr 55 | test-const_str_hash.strhash 56 | test-payload-tracker 57 | test-transcode 58 | dtmf_rx_fillin.h 59 | *-test.c 60 | jitter_buffer.c 61 | t38.c 62 | spandsp_recv_fax_pcm 63 | spandsp_recv_fax_t38 64 | spandsp_send_fax_pcm 65 | spandsp_send_fax_t38 66 | spandsp_logging.h 67 | aead-aes-crypt 68 | tcp_listener.c 69 | test-kernel-module 70 | test-resample 71 | mqtt.c 72 | cli.c 73 | janus.c 74 | websocket.c 75 | test-stats 76 | ssllib.c 77 | time-fudge-preload.so 78 | mvr2s_x64_avx2.S 79 | mvr2s_x64_avx512.S 80 | test-mix-buffer 81 | mix_buffer.c 82 | audio_player.c 83 | mix_in_x64_avx2.S 84 | mix_in_x64_avx512bw.S 85 | mix_in_x64_sse2.S 86 | test-amr-decode 87 | test-amr-encode 88 | bufferpool.c 89 | uring.c 90 | aead-decrypt 91 | arena.c 92 | ng_client.c 93 | -------------------------------------------------------------------------------- /t/aead-decrypt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "crypto.h" 4 | #include "rtplib.h" 5 | #include "log.h" 6 | #include "main.h" 7 | #include "ssrc.h" 8 | #include "rtp.h" 9 | #include "../kernel-module/common_stats.h" 10 | 11 | 12 | struct rtpengine_config rtpe_config = { 13 | }; 14 | int get_local_log_level(unsigned int u) { 15 | return -1; 16 | } 17 | 18 | int main(int argc, char **argv) { 19 | if (argc < 5) { 20 | printf("Usage: %s \n", argv[0]); 21 | printf("Example: %s AEAD_AES_256_GCM \\\n", argv[0]); 22 | printf(" F4mVFvZGU/S50OXT17xvKHCC/8CV5vgp8OgmAlFpKcc= \\\n"); 23 | printf(" fNAN6151Wc6DgFEZ gAhqUYthx4ivfuWtbtq...\n"); 24 | return 1; 25 | } 26 | 27 | crypto_init_main(); 28 | 29 | str suite; 30 | suite = STR(argv[1]); 31 | struct crypto_context cc = {0}; 32 | cc.params.crypto_suite = crypto_find_suite(&suite); 33 | assert(cc.params.crypto_suite); 34 | 35 | const char *key64 = argv[2]; 36 | const char *salt64 = argv[3]; 37 | 38 | size_t len; 39 | uint8_t *key = g_base64_decode(key64, &len); 40 | assert(len == cc.params.crypto_suite->master_key_len); 41 | uint8_t *salt = g_base64_decode(salt64, &len); 42 | assert(len == cc.params.crypto_suite->master_salt_len); 43 | 44 | memcpy(cc.params.master_key, key, cc.params.crypto_suite->master_key_len); 45 | memcpy(cc.params.master_salt, salt, cc.params.crypto_suite->master_salt_len); 46 | 47 | const char *pack64 = argv[4]; 48 | uint8_t *pack = g_base64_decode(pack64, &len); 49 | str s = STR_LEN((char *) pack, len); 50 | 51 | unsigned int roc = 0; 52 | 53 | if (argc >= 6) 54 | roc = atoi(argv[5]); 55 | 56 | struct ssrc_stats stats = { 57 | .ext_seq = roc << 16, 58 | }; 59 | 60 | struct ssrc_entry_call se = { 61 | .stats = &stats, 62 | }; 63 | 64 | int ret = rtp_savp2avp(&s, &cc, &se); 65 | assert(ret == 0); 66 | printf("idx %d ROC %d\n", se.stats->ext_seq, se.stats->ext_seq >> 16); 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /t/auto-daemon-tests-templ-def-offer.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use NGCP::Rtpclient::SRTP; 7 | use NGCP::Rtpengine::AutoTest; 8 | use Test::More; 9 | use Test2::Tools::Compare qw(); 10 | use NGCP::Rtpclient::ICE; 11 | use POSIX; 12 | 13 | 14 | autotest_start(qw(--config-file=test3.conf)) or die; 15 | 16 | 17 | 18 | 19 | my ($sock_a, $sock_b, $sock_c, $sock_d, $port_a, $port_b, $ssrc, $ssrc_b, $resp, 20 | $sock_ax, $sock_bx, $port_ax, $port_bx, 21 | $srtp_ctx_a, $srtp_ctx_b, $srtp_ctx_a_rev, $srtp_ctx_b_rev, $ufrag_a, $ufrag_b, 22 | @ret1, @ret2, @ret3, @ret4, $srtp_key_a, $srtp_key_b, $ts, $seq, $has_recv); 23 | 24 | 25 | 26 | new_call; 27 | 28 | offer('template', { }, <len > 0) 21 | g_string_append(dtmf_logs, "\n"); 22 | g_string_append_len(dtmf_logs, s->str, s->len); 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /t/log_funcs.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOG_FUNCS_H_ 2 | #define _LOG_FUNCS_H_ 3 | 4 | #include "helpers.h" 5 | #include "str.h" 6 | #include "types.h" 7 | 8 | struct ice_agent; 9 | struct call_media; 10 | 11 | INLINE void log_info_reset(void) { 12 | } 13 | INLINE void log_info_pop(void) { 14 | } 15 | INLINE void log_info_pop_until(void *p) { 16 | } 17 | INLINE void log_info_call(call_t *c) { 18 | } 19 | INLINE void log_info_stream_fd(stream_fd *sfd) { 20 | } 21 | INLINE void log_info_str(const str *s) { 22 | } 23 | INLINE void log_info_c_string(const char *s) { 24 | } 25 | INLINE void log_info_ice_agent(struct ice_agent *ag) { 26 | } 27 | INLINE void log_info_media(struct call_media *m) { 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /t/loglevels.h: -------------------------------------------------------------------------------- 1 | ll(core, "Everything that isn't part of another subsystem") 2 | ll(spandsp, "Log messages generated by SpanDSP directly") 3 | ll(ffmpeg, "Log messages generated by ffmpeg directly") 4 | ll(transcoding, "Media and RTP transcoding") 5 | ll(codec, "Codec negotiation") 6 | ll(rtcp, "RTCP handling") 7 | ll(ice, "ICE negotiation") 8 | ll(crypto, "Negotiation of SRTP, crypto suites, DTLS, SDES") 9 | ll(srtp, "SRTP encryption and decryption") 10 | ll(internals, "Noisy low-level internals") 11 | ll(http, "HTTP, HTTPS, Websockets") 12 | ll(control, "Control protocols including SDP exchanges, CLI") 13 | ll(dtx, "DTX timer/buffer") 14 | -------------------------------------------------------------------------------- /t/pcm_rtp_test.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use IPC::Open3; 6 | use IO::Socket; 7 | use IO::Socket::IP; 8 | 9 | my $laddr = shift or die; 10 | my $lport = shift or die; 11 | my $raddr = shift or die; 12 | my $rport = shift or die; 13 | 14 | my $sock = IO::Socket::IP->new(Type => &SOCK_DGRAM, Proto => 'udp', 15 | LocalHost => $laddr, LocalPort => $lport, 16 | PeerHost => $raddr, PeerPort => $rport, 17 | ) 18 | or die; 19 | 20 | my ($src, $sink); 21 | my $pid = open3($sink, $src, '>&STDERR', @ARGV) or die; 22 | 23 | my ($playsrc, $playsink); 24 | open($playsrc, '|-', qw(play -q -c 1 -e a-law -r 8000 -t raw -)) or die; 25 | open($playsink, '|-', qw(play -q -c 1 -e a-law -r 8000 -t raw -)) or die; 26 | 27 | my $lseq = rand(65536); 28 | my $lssrc = rand(65536); 29 | my $lts = rand(2*32); 30 | my $lpt = 8; # PCMA 31 | my $lmark = 0x80; 32 | my $rseq = -1; 33 | my $rts = -1; 34 | 35 | while (1) { 36 | my $buf; 37 | 38 | last unless sysread($src, $buf = '', 160); 39 | syswrite($playsrc, $buf); 40 | 41 | my $rtp = pack('CCnNN a*', 0x80, $lpt | $lmark, $lseq, $lts, $lssrc, $buf); 42 | last unless $sock->syswrite($rtp) or last; 43 | $lseq++; 44 | $lts += 160; 45 | $lmark = 0x00; 46 | 47 | last unless $sock->sysread($buf = '', 0xffff); 48 | 49 | my ($ver, $rpt, $seq, $ts, $rssrc, $payload) = unpack('CCnNN a*', $buf); 50 | die unless length($payload) == 160; 51 | die unless ($rpt & 0x7f) == $lpt; 52 | die unless ($rseq == -1 || (($rseq + 1) & 0xffff) == $seq); 53 | die unless ($rts == -1 || (($rts + 160) & 0xffffffff) == $ts); 54 | syswrite($playsink, $payload); 55 | $rseq = $seq; 56 | $rts = $ts; 57 | 58 | last unless syswrite($sink, $payload); 59 | } 60 | -------------------------------------------------------------------------------- /t/spandsp_fax_pcm_test.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use IPC::Open2; 6 | use POSIX ":sys_wait_h"; 7 | 8 | my ($send_src, $send_sink); 9 | my $send_pid = open2($send_src, $send_sink, './spandsp_send_fax_pcm test.tif') or die; 10 | 11 | unlink('out.tif'); 12 | 13 | my ($recv_src, $recv_sink); 14 | my $recv_pid = open2($recv_src, $recv_sink, './spandsp_recv_fax_pcm out.tif') or die; 15 | 16 | while ($send_pid && $recv_pid) { 17 | 18 | my $buf; 19 | 20 | if (sysread($send_src, $buf = '', 160)) { 21 | syswrite($recv_sink, $buf) or die; 22 | } 23 | 24 | if (sysread($recv_src, $buf = '', 160)) { 25 | syswrite($send_sink, $buf) or die; 26 | } 27 | 28 | undef($send_pid) if waitpid($send_pid, WNOHANG); 29 | undef($recv_pid) if waitpid($recv_pid, WNOHANG); 30 | } 31 | 32 | sleep(5); 33 | -------------------------------------------------------------------------------- /t/spandsp_fax_t38_test.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use IPC::Open2; 6 | use POSIX ":sys_wait_h"; 7 | 8 | my ($send_src, $send_sink); 9 | my $send_pid = open2($send_src, $send_sink, './spandsp_send_fax_t38 test.tif') or die; 10 | 11 | unlink('out.tif'); 12 | 13 | my ($recv_src, $recv_sink); 14 | my $recv_pid = open2($recv_src, $recv_sink, './spandsp_recv_fax_t38 out.tif') or die; 15 | 16 | while ($send_pid && $recv_pid) { 17 | 18 | my ($buf, $rin); 19 | 20 | $rin = ''; 21 | vec($rin, fileno($send_src), 1) = 1; 22 | while (select(my $rout = $rin, undef, undef, 0.02) == 1) { 23 | sysread($send_src, $buf = '', 1); 24 | syswrite($recv_sink, $buf) or last; 25 | } 26 | 27 | $rin = ''; 28 | vec($rin, fileno($recv_src), 1) = 1; 29 | while (select(my $rout = $rin, undef, undef, 0.02) == 1) { 30 | sysread($recv_src, $buf = '', 1); 31 | syswrite($send_sink, $buf) or last; 32 | } 33 | 34 | undef($send_pid) if waitpid($send_pid, WNOHANG); 35 | undef($recv_pid) if waitpid($recv_pid, WNOHANG); 36 | } 37 | 38 | sleep(5); 39 | -------------------------------------------------------------------------------- /t/t38_udptl_test.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use IPC::Open3; 6 | use IO::Socket; 7 | use IO::Socket::IP; 8 | 9 | my $laddr = shift or die; 10 | my $lport = shift or die; 11 | my $raddr = shift or die; 12 | my $rport = shift or die; 13 | 14 | my $sock = IO::Socket::IP->new(Type => &SOCK_DGRAM, Proto => 'udp', 15 | LocalHost => $laddr, LocalPort => $lport, 16 | PeerHost => $raddr, PeerPort => $rport, 17 | ) 18 | or die; 19 | 20 | my $devnull; 21 | die unless open($devnull, '>', '/dev/null'); 22 | 23 | my ($src, $sink); 24 | my $pid = open3($sink, $src, ">&".fileno($devnull), @ARGV) or die; 25 | 26 | my $lseq = 0; 27 | my $rseq = 0; 28 | my $srcbuf = ''; 29 | 30 | local $| = 1; 31 | 32 | while (1) { 33 | my $rin = ''; 34 | vec($rin, fileno($src), 1) = 1; 35 | while (select(my $rout = $rin, undef, undef, 0.01) == 1) { 36 | my $ret = sysread($src, my $buf, 1); 37 | last unless $ret; 38 | $srcbuf .= $buf; 39 | my ($seq_out, $len, $pkt) = unpack('SSa*', $srcbuf); 40 | next unless defined($pkt); 41 | next if length($pkt) < $len; 42 | 43 | substr($srcbuf, 0, $len + 4) = ''; 44 | substr($pkt, $len) = ''; 45 | 46 | my $udptl = pack('nCa*Ca*Ca*', $seq_out, length($pkt), $pkt, 0x00, 47 | '', 0, ''); 48 | 49 | print('!'); 50 | last unless $sock->syswrite($udptl); 51 | } 52 | 53 | $rin = ''; 54 | vec($rin, fileno($sock), 1) = 1; 55 | while (select(my $rout = $rin, undef, undef, 0.01) == 1) { 56 | my $ret = $sock->sysread(my $buf, 0xffff); 57 | my ($seq, $len, $pkt) = unpack('nCa*', $buf); 58 | my $t38 = substr($pkt, 0, $len); 59 | 60 | print('.'); 61 | last unless syswrite($sink, pack('SSa*', $seq, length($t38), $t38)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /t/test-basic-ipv4-6.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | {sockdomain => &Socket::AF_INET}, 11 | {sockdomain => &Socket::AF_INET6} 12 | ); 13 | 14 | $r->timer_once(3, sub { 15 | $b->answer($a, ICE => 'remove', label => "callee"); 16 | $a->start_rtp(); 17 | $a->start_rtcp(); 18 | }); 19 | $r->timer_once(10, sub { $r->stop(); }); 20 | 21 | $a->offer($b, ICE => 'remove', label => "caller", 'address-family' => 'IP6'); 22 | $b->start_rtp(); 23 | $b->start_rtcp(); 24 | 25 | $r->run(); 26 | 27 | $a->teardown(dump => 1); 28 | -------------------------------------------------------------------------------- /t/test-basic-ipv4.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | {sockdomain => &Socket::AF_INET}, 11 | {sockdomain => &Socket::AF_INET} 12 | ); 13 | 14 | $r->timer_once(3, sub { 15 | $b->answer($a, ICE => 'remove', label => "callee"); 16 | $a->start_rtp(); 17 | $a->start_rtcp(); 18 | }); 19 | $r->timer_once(10, sub { $r->stop(); }); 20 | 21 | $a->offer($b, ICE => 'remove', label => "caller"); 22 | $b->start_rtp(); 23 | $b->start_rtcp(); 24 | 25 | $r->run(); 26 | 27 | $a->teardown(dump => 1); 28 | -------------------------------------------------------------------------------- /t/test-basic-ipv6-4.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | {sockdomain => &Socket::AF_INET6}, 11 | {sockdomain => &Socket::AF_INET} 12 | ); 13 | 14 | $r->timer_once(3, sub { 15 | $b->answer($a, ICE => 'remove', label => "callee"); 16 | $a->start_rtp(); 17 | $a->start_rtcp(); 18 | }); 19 | $r->timer_once(10, sub { $r->stop(); }); 20 | 21 | $a->offer($b, ICE => 'remove', label => "caller", 'address-family' => 'IP4'); 22 | $b->start_rtp(); 23 | $b->start_rtcp(); 24 | 25 | $r->run(); 26 | 27 | $a->teardown(dump => 1); 28 | -------------------------------------------------------------------------------- /t/test-basic-ipv6.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | {sockdomain => &Socket::AF_INET6}, 11 | {sockdomain => &Socket::AF_INET6} 12 | ); 13 | 14 | $r->timer_once(3, sub { 15 | $b->answer($a, ICE => 'remove', label => "callee"); 16 | $a->start_rtp(); 17 | $a->start_rtcp(); 18 | }); 19 | $r->timer_once(10, sub { $r->stop(); }); 20 | 21 | $a->offer($b, ICE => 'remove', label => "caller"); 22 | $b->start_rtp(); 23 | $b->start_rtcp(); 24 | 25 | $r->run(); 26 | 27 | $a->teardown(dump => 1); 28 | -------------------------------------------------------------------------------- /t/test-const_str_hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "str.h" 4 | 5 | static int test_hash(char *p) { 6 | str s; 7 | s = STR(p); 8 | switch (__csh_lookup(&s)) { 9 | case CSH_LOOKUP("one"): 10 | return 1; 11 | case CSH_LOOKUP("two"): 12 | return 2; 13 | case CSH_LOOKUP("dashed-string"): 14 | return 3; 15 | default: 16 | return 0; 17 | } 18 | // STR_LOOKUP("one") // catch duplicate 19 | } 20 | 21 | static void test(char *p, int exp) { 22 | int h = test_hash(p); 23 | if (h != exp) { 24 | printf("%s:%i test failed: %u != %u (string '%s')\n", __FILE__, __LINE__, h, exp, p); 25 | abort(); 26 | } 27 | } 28 | 29 | int main(void) { 30 | test("one", 1); 31 | test("two", 2); 32 | test("dashed-string", 3); 33 | test("doesn't exist", 0); 34 | return 0; 35 | } 36 | 37 | int get_local_log_level(unsigned int u) { 38 | return -1; 39 | } 40 | -------------------------------------------------------------------------------- /t/test-dtls.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | 7 | my $r = NGCP::Rtpengine::Test->new(); 8 | my $a = $r->client(dtls => 1); 9 | my $b = $r->client(); 10 | 11 | $r->timer_once(3, sub { $b->answer($a) }); 12 | 13 | $a->offer($b, 'transport-protocol' => 'RTP/AVP'); 14 | 15 | $r->run(); 16 | -------------------------------------------------------------------------------- /t/test-ice.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | 7 | my $r = NGCP::Rtpengine::Test->new(); 8 | my $a = $r->client(ice => 1); 9 | my $b = $r->client(sockdomain => &Socket::AF_INET); 10 | 11 | $r->timer_once(3, sub { $b->answer($a) }); 12 | $r->timer_once(5, sub { $a->start_rtp(); $b->start_rtp(); }); 13 | 14 | $a->offer($b, ICE => 'remove', 'address-family' => 'IP4'); 15 | 16 | $r->run(); 17 | -------------------------------------------------------------------------------- /t/test-interfaces.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $iterations = 10; 9 | my @interfaces = qw(int ext); 10 | my @domains = (&Socket::AF_INET); 11 | 12 | my $r = NGCP::Rtpengine::Test->new(media_port => 50000); 13 | 14 | for my $a_domain (@domains) { 15 | for my $b_domain (@domains) { 16 | if (!@interfaces) { 17 | for (1 .. $iterations) { 18 | run_test([], $a_domain, $b_domain); 19 | } 20 | } 21 | else { 22 | for my $a_interface (@interfaces) { 23 | for my $b_interface (@interfaces) { 24 | for (1 .. $iterations) { 25 | run_test([$a_interface, $b_interface], $a_domain, $b_domain); 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | 33 | sub run_test { 34 | my ($directions, $a_domain, $b_domain) = @_; 35 | print("Testing directions @{$directions} between $a_domain and $b_domain\n"); 36 | 37 | my ($a, $b) = $r->client_pair( 38 | {sockdomain => $a_domain}, 39 | {sockdomain => $b_domain} 40 | ); 41 | 42 | print("Offering with address: " . $a->{sockets}->[0]->[0]->sockhost . "\n"); 43 | my %dir_arg = (); 44 | $dir_arg{direction} = $directions if @{$directions}; 45 | $a->offer($b, ICE => 'remove', label => "caller", %dir_arg); 46 | print("Offer out with address: " . $b->{remote_media}->connection->{address} . "\n"); 47 | 48 | print("Answering with address: " . $b->{sockets}->[0]->[0]->sockhost . "\n"); 49 | $b->answer($a, ICE => 'remove', label => "callee"); 50 | print("Answer out with address: " . $a->{remote_media}->connection->{address} . "\n"); 51 | 52 | $a->teardown(); 53 | 54 | print("\n"); 55 | } 56 | -------------------------------------------------------------------------------- /t/test-packetloss.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | { 11 | sockdomain => &Socket::AF_INET, 12 | packetloss => 5, 13 | }, 14 | { 15 | sockdomain => &Socket::AF_INET, 16 | packetloss => 10, 17 | } 18 | ); 19 | 20 | $r->timer_once(1, sub { 21 | $b->answer($a, ICE => 'remove'); 22 | $a->start_rtp(); 23 | $a->start_rtcp(); 24 | }); 25 | $r->timer_once(60, sub { $r->stop(); }); 26 | 27 | $a->offer($b, ICE => 'remove'); 28 | $b->start_rtp(); 29 | $b->start_rtcp(); 30 | 31 | $r->run(); 32 | 33 | $a->teardown(dump => 1); 34 | -------------------------------------------------------------------------------- /t/test-plain-sdes-full.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | {}, 11 | {sdes => 1} 12 | ); 13 | 14 | @{$b->{sdes}->{suites}} >= 7 or die; # all that we support 15 | 16 | $r->timer_once(10, sub { $r->stop(); }); 17 | 18 | $a->offer($b, ICE => 'remove', 'transport-protocol' => 'RTP/SAVP'); 19 | 20 | @{$b->{sdes}->{remote_suites}} >= 7 or die; # all that we support 21 | 22 | $b->answer($a, ICE => 'remove'); 23 | 24 | @{$b->{sdes}->{remote_suites}} >= 7 or die; # still the full list 25 | @{$b->{sdes}->{suites}} >= 7 or die; # still the full list 26 | 27 | $a->start_rtp(); 28 | $a->start_rtcp(); 29 | $b->start_rtp(); 30 | $b->start_rtcp(); 31 | 32 | $r->run(); 33 | 34 | $a->teardown(); 35 | -------------------------------------------------------------------------------- /t/test-plain-sdes.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | {}, 11 | {sdes => 1} 12 | ); 13 | 14 | @{$b->{sdes}->{suites}} >= 7 or die; # all that we support 15 | 16 | $r->timer_once(10, sub { $r->stop(); }); 17 | 18 | $a->offer($b, ICE => 'remove', 'transport-protocol' => 'RTP/SAVP'); 19 | 20 | @{$b->{sdes}->{remote_suites}} >= 7 or die; # all that we support 21 | $b->{sdes}->trim(); 22 | @{$b->{sdes}->{remote_suites}} == 1 or die; # just 1 for answer 23 | @{$b->{sdes}->{suites}} == 1 or die; # just 1 for answer 24 | 25 | $b->answer($a, ICE => 'remove'); 26 | 27 | @{$b->{sdes}->{remote_suites}} == 1 or die; # just 1 answer 28 | @{$b->{sdes}->{suites}} == 1 or die; # just 1 after negotiation 29 | 30 | $a->start_rtp(); 31 | $a->start_rtcp(); 32 | $b->start_rtp(); 33 | $b->start_rtcp(); 34 | 35 | $r->run(); 36 | 37 | $a->teardown(); 38 | -------------------------------------------------------------------------------- /t/test-resample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "resample.h" 5 | #include "codeclib.h" 6 | #include "fix_frame_channel_layout.h" 7 | #include "main.h" 8 | 9 | struct rtpengine_config rtpe_config; 10 | struct rtpengine_config initial_rtpe_config; 11 | 12 | void test_1(int in_samples, int in_format, int in_rate, int in_channels, bool no_filter, 13 | int out_format, int out_rate, int out_channels, 14 | int out_exp_samples) 15 | { 16 | printf("testing %i %i %i %i %i %i %i %i\n", in_samples, in_format, in_rate, in_channels, 17 | out_format, out_rate, out_channels, 18 | out_exp_samples); 19 | 20 | AVFrame *in_f = av_frame_alloc(); 21 | in_f->nb_samples = in_samples; 22 | in_f->format = in_format; 23 | in_f->sample_rate = in_rate; 24 | DEF_CH_LAYOUT(&in_f->CH_LAYOUT, in_channels); 25 | int ret = av_frame_get_buffer(in_f, 0); 26 | assert(ret == 0); 27 | memset(in_f->extended_data[0], 0, in_f->nb_samples * av_get_bytes_per_sample(in_f->format)); 28 | 29 | resample_t resampler; 30 | ZERO(resampler); 31 | resampler.no_filter = no_filter; 32 | 33 | format_t out_fmt = { 34 | .channels = out_channels, 35 | .clockrate = out_rate, 36 | .format = out_format, 37 | }; 38 | AVFrame *out_f = resample_frame(&resampler, in_f, &out_fmt); 39 | assert(out_f != NULL); 40 | 41 | printf("received samples %i\n", out_f->nb_samples); 42 | assert(out_f->nb_samples == out_exp_samples); 43 | 44 | av_frame_free(&in_f); 45 | av_frame_free(&out_f); 46 | resample_shutdown(&resampler); 47 | } 48 | 49 | int main(void) { 50 | rtpe_common_config_ptr = &rtpe_config.common; 51 | codeclib_init(0); 52 | 53 | test_1(320, AV_SAMPLE_FMT_S16, 16000, 1, false, AV_SAMPLE_FMT_S16, 8000, 1, 144); 54 | test_1(160, AV_SAMPLE_FMT_S16, 8000, 1, false, AV_SAMPLE_FMT_S16, 16000, 1, 288); 55 | 56 | test_1(320, AV_SAMPLE_FMT_S16, 16000, 1, true, AV_SAMPLE_FMT_S16, 8000, 1, 160); 57 | test_1(160, AV_SAMPLE_FMT_S16, 8000, 1, true, AV_SAMPLE_FMT_S16, 16000, 1, 320); 58 | 59 | return 0; 60 | } 61 | 62 | int get_local_log_level(unsigned int u) { 63 | return 7; 64 | } 65 | -------------------------------------------------------------------------------- /t/test-sdes-plain.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | {sdes => 1}, 11 | {} 12 | ); 13 | 14 | @{$a->{sdes}->{suites}} >= 7 or die; # all that we support 15 | 16 | $r->timer_once(10, sub { $r->stop(); }); 17 | 18 | $a->offer($b, ICE => 'remove', 'transport-protocol' => 'RTP/AVP'); 19 | 20 | @{$a->{sdes}->{suites}} >= 7 or die; # all that we support 21 | 22 | $b->answer($a, ICE => 'remove'); 23 | 24 | @{$a->{sdes}->{remote_suites}} == 1 or die; # just 1 answer 25 | @{$a->{sdes}->{suites}} == 1 or die; # just 1 after negotiation 26 | 27 | $a->start_rtp(); 28 | $a->start_rtcp(); 29 | $b->start_rtp(); 30 | $b->start_rtcp(); 31 | 32 | $r->run(); 33 | 34 | $a->teardown(); 35 | -------------------------------------------------------------------------------- /t/test-sdes-sdes-select-offer-restrict.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | {sdes => 1, sdes_args => {suites => [qw( 11 | AES_256_CM_HMAC_SHA1_80 AES_256_CM_HMAC_SHA1_32 12 | AES_CM_128_HMAC_SHA1_80 AES_CM_128_HMAC_SHA1_32 13 | )]}}, 14 | {sdes => 1} 15 | ); 16 | 17 | @{$a->{sdes}->{suites}} == 4 or die; # the ones we selected 18 | @{$b->{sdes}->{suites}} >= 7 or die; # all that we support 19 | 20 | $r->timer_once(10, sub { $r->stop(); }); 21 | 22 | $a->offer($b, ICE => 'remove', flags => [qw(SDES-no-AES_256_CM_HMAC_SHA1_80 SDES-no-AES_256_CM_HMAC_SHA1_32)]); 23 | 24 | @{$a->{sdes}->{suites}} == 4 or die; # the ones we selected 25 | 26 | @{$b->{sdes}->{remote_suites}} >= 5 or die; # all that we support (our selected + added by rtpengine - restrict) 27 | $b->{sdes}->trim(); 28 | @{$b->{sdes}->{remote_suites}} == 1 or die; # just 1 for answer 29 | @{$b->{sdes}->{suites}} == 1 or die; # just 1 for answer 30 | 31 | $b->answer($a, ICE => 'remove'); 32 | 33 | @{$a->{sdes}->{remote_suites}} == 1 or die; # just 1 answer 34 | @{$a->{sdes}->{suites}} == 1 or die; # just 1 after negotiation 35 | 36 | @{$b->{sdes}->{remote_suites}} == 1 or die; # just 1 answer 37 | @{$b->{sdes}->{suites}} == 1 or die; # just 1 after negotiation 38 | 39 | $a->start_rtp(); 40 | $a->start_rtcp(); 41 | $b->start_rtp(); 42 | $b->start_rtcp(); 43 | 44 | $r->run(); 45 | 46 | $a->teardown(); 47 | -------------------------------------------------------------------------------- /t/test-sdes-sdes-select-offer.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | {sdes => 1, sdes_args => {suites => [qw( 11 | AES_256_CM_HMAC_SHA1_80 AES_256_CM_HMAC_SHA1_32 12 | AES_CM_128_HMAC_SHA1_80 AES_CM_128_HMAC_SHA1_32 13 | )]}}, 14 | {sdes => 1} 15 | ); 16 | 17 | @{$a->{sdes}->{suites}} == 4 or die; # the ones we selected 18 | @{$b->{sdes}->{suites}} >= 7 or die; # all that we support 19 | 20 | $r->timer_once(10, sub { $r->stop(); }); 21 | 22 | $a->offer($b, ICE => 'remove'); 23 | 24 | @{$a->{sdes}->{suites}} == 4 or die; # the ones we selected 25 | 26 | @{$b->{sdes}->{remote_suites}} >= 7 or die; # all that we support (our selected + added by rtpengine) 27 | $b->{sdes}->trim(); 28 | @{$b->{sdes}->{remote_suites}} == 1 or die; # just 1 for answer 29 | @{$b->{sdes}->{suites}} == 1 or die; # just 1 for answer 30 | 31 | $b->answer($a, ICE => 'remove'); 32 | 33 | @{$a->{sdes}->{remote_suites}} == 1 or die; # just 1 answer 34 | @{$a->{sdes}->{suites}} == 1 or die; # just 1 after negotiation 35 | 36 | @{$b->{sdes}->{remote_suites}} == 1 or die; # just 1 answer 37 | @{$b->{sdes}->{suites}} == 1 or die; # just 1 after negotiation 38 | 39 | $a->start_rtp(); 40 | $a->start_rtcp(); 41 | $b->start_rtp(); 42 | $b->start_rtcp(); 43 | 44 | $r->run(); 45 | 46 | $a->teardown(); 47 | -------------------------------------------------------------------------------- /t/test-sdes-sdes.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | {sdes => 1}, 11 | {sdes => 1} 12 | ); 13 | 14 | @{$a->{sdes}->{suites}} >= 7 or die; # all that we support 15 | @{$b->{sdes}->{suites}} >= 7 or die; # all that we support 16 | 17 | $r->timer_once(10, sub { $r->stop(); }); 18 | 19 | $a->offer($b, ICE => 'remove'); 20 | 21 | @{$a->{sdes}->{suites}} >= 7 or die; # all that we support 22 | 23 | @{$b->{sdes}->{remote_suites}} >= 7 or die; # all that we support 24 | $b->{sdes}->trim(); 25 | @{$b->{sdes}->{remote_suites}} == 1 or die; # just 1 for answer 26 | @{$b->{sdes}->{suites}} == 1 or die; # just 1 for answer 27 | 28 | $b->answer($a, ICE => 'remove'); 29 | 30 | @{$a->{sdes}->{remote_suites}} == 1 or die; # just 1 answer 31 | @{$a->{sdes}->{suites}} == 1 or die; # just 1 after negotiation 32 | 33 | @{$b->{sdes}->{remote_suites}} == 1 or die; # just 1 answer 34 | @{$b->{sdes}->{suites}} == 1 or die; # just 1 after negotiation 35 | 36 | $a->start_rtp(); 37 | $a->start_rtcp(); 38 | $b->start_rtp(); 39 | $b->start_rtcp(); 40 | 41 | $r->run(); 42 | 43 | $a->teardown(); 44 | -------------------------------------------------------------------------------- /t/test-transcode.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(); 9 | my ($a, $b) = $r->client_pair( 10 | {sockdomain => &Socket::AF_INET, codecs => [qw(PCMU)], no_data_check => 1}, 11 | {sockdomain => &Socket::AF_INET, codecs => [qw(G722)], no_data_check => 1} 12 | ); 13 | 14 | $r->timer_once(3, sub { 15 | $b->answer($a, ICE => 'remove', label => "callee"); 16 | $a->remote_codecs() eq 'PCMU/8000/1' or die; 17 | $a->send_codecs() eq 'PCMU/8000/1' or die; 18 | $a->start_rtp(); 19 | $a->start_rtcp(); 20 | }); 21 | $r->timer_once(10, sub { $r->stop(); }); 22 | 23 | $a->offer($b, ICE => 'remove', label => "caller", codec => { transcode => ['G722']}, flags => [qw(record-call)]); 24 | $b->remote_codecs() eq 'PCMU/8000/1,G722/8000/1' or die; 25 | $b->send_codecs() eq 'G722/8000/1' or die; 26 | $b->start_rtp(); 27 | $b->start_rtcp(); 28 | 29 | $r->run(); 30 | 31 | $a->teardown(dump => 1); 32 | -------------------------------------------------------------------------------- /t/test-unidirectional.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use NGCP::Rtpengine::Test; 6 | use IO::Socket; 7 | 8 | my $r = NGCP::Rtpengine::Test->new(host => '192.168.1.128'); 9 | my ($a, $b) = $r->client_pair( 10 | {sockdomain => &Socket::AF_INET}, 11 | {sockdomain => &Socket::AF_INET} 12 | ); 13 | 14 | $r->timer_once(3, sub { $b->answer($a, ICE => 'remove', flags => ['unidirectional']); }); 15 | $r->timer_once(10, sub { $r->stop(); }); 16 | 17 | $a->offer($b, ICE => 'remove', flags => ['unidirectional']); 18 | $b->start_rtp(); 19 | 20 | $r->run(); 21 | 22 | $a->teardown(); 23 | -------------------------------------------------------------------------------- /t/test.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sipwise/rtpengine/bad2b3df1d85d70cb89c4020b9aa7ba5578f9607/t/test.tif -------------------------------------------------------------------------------- /t/test1.conf: -------------------------------------------------------------------------------- 1 | [rtpengine] 2 | table = -1 3 | interface = 203.0.113.1 ; 2001:db8:4321::1 ; 203.0.113.2 ; 2001:db8:4321::2 ; foobar/203.0.113.3 ; quux/203.0.113.4 4 | listen-ng = 2223 5 | foreground = true 6 | log-level = 7 7 | log-stderr = true 8 | templates = templates 9 | interfaces-config = interface 10 | 11 | [templates] 12 | WebRTC = transport-protocol=UDP/TLS/RTP/SAVPF ICE=force trickle-ICE rtcp-mux=[offer require] no-rtcp-attribute SDES=off generate-mid 13 | 14 | [interface-overlap-A] 15 | address = 203.0.113.6 16 | port-min = 3000 17 | port-max = 3007 18 | 19 | [interface-overlap-B] 20 | address = 203.0.113.6 21 | port-min = 3004 22 | port-max = 3011 23 | -------------------------------------------------------------------------------- /t/test2.conf: -------------------------------------------------------------------------------- 1 | [rtpengine] 2 | table = -1 3 | interface = 203.0.113.1 ; 2001:db8:4321::1 ; 203.0.113.2 ; 2001:db8:4321::2 ; foobar/203.0.113.3 ; quux/203.0.113.4 4 | listen-ng = 2223 5 | foreground = true 6 | log-level = 7 7 | log-stderr = true 8 | templates = templates 9 | 10 | [templates] 11 | default = transport-protocol=UDP/TLS/RTP/SAVPF ICE=force trickle-ICE rtcp-mux=[offer require] no-rtcp-attribute SDES=off generate-mid 12 | -------------------------------------------------------------------------------- /t/test3.conf: -------------------------------------------------------------------------------- 1 | [rtpengine] 2 | table = -1 3 | interfaces-config = interface 4 | listen-ng = 2223 5 | foreground = true 6 | log-level = 7 7 | log-stderr = true 8 | templates = templates 9 | 10 | [templates] 11 | offer = transport-protocol=UDP/TLS/RTP/SAVPF ICE=force trickle-ICE rtcp-mux=[offer require] no-rtcp-attribute SDES=off generate-mid 12 | 13 | [interface-default] 14 | address = 203.0.113.1 15 | 16 | [interface-default-2] 17 | name = default 18 | address = 2001:db8:4321::1 19 | 20 | [interface-default-3] 21 | name = default 22 | address = 203.0.113.2 23 | 24 | [interface-default-4] 25 | name = default 26 | address = 2001:db8:4321::2 27 | 28 | [interface-foobar] 29 | address = 203.0.113.3 30 | 31 | [interface-quux] 32 | address = 203.0.113.4 33 | -------------------------------------------------------------------------------- /t/test4.conf: -------------------------------------------------------------------------------- 1 | [rtpengine] 2 | table = -1 3 | interfaces-config = interface 4 | listen-ng = 2223 5 | foreground = true 6 | log-level = 7 7 | log-stderr = true 8 | transcode-config = tc 9 | 10 | [interface-default-4] 11 | name = default 12 | address = 203.0.113.1 13 | 14 | [interface-default-6] 15 | name = default 16 | address = 2001:db8:4321::1 17 | 18 | [interface-rev-4] 19 | name = rev 20 | address = 2001:db8:4321::1 21 | 22 | [interface-rev-6] 23 | name = rev 24 | address = 203.0.113.1 25 | 26 | [tc-PCMA-PCMU] 27 | source = PCMA 28 | destination = PCMU 29 | transform = 203.0.113.42:3334 30 | local-interface = default 31 | remote-interface = default 32 | 33 | [tc-PCMU-G722] 34 | source = PCMU 35 | destination = G722 36 | transform = 203.0.113.42:3334 37 | local-interface = default 38 | remote-interface = default 39 | -------------------------------------------------------------------------------- /t/test5.conf: -------------------------------------------------------------------------------- 1 | [rtpengine] 2 | table = -1 3 | interface = 203.0.113.1 ; 2001:db8:4321::1 ; foo/203.0.113.7 ; bar/203.0.113.8 4 | listen-ng = 2223 5 | listen-cli = 12345 6 | listen-udp = 2222 7 | foreground = true 8 | log-level = 7 9 | log-stderr = true 10 | silence-detect = 1 11 | 12 | # controls the value to be added to the session level of SDP 13 | # whenever MoH is triggered. If not defined, then not in use. 14 | moh-attr-name = rtpengine-hold 15 | 16 | # protects against double MoH played 17 | # (e.g. when inadvertently two rtpengine instances try to trigger MoH) 18 | moh-prevent-double-hold = true 19 | -------------------------------------------------------------------------------- /t/test6.conf: -------------------------------------------------------------------------------- 1 | [rtpengine] 2 | table = -1 3 | interface = 203.0.113.1 4 | listen-ng = 2223 5 | foreground = true 6 | log-level = 7 7 | log-stderr = true 8 | transcode-config = tc 9 | 10 | [tc-PCMU-opus-lower] 11 | source = PCMU 12 | destination = opus 13 | preference = -5 14 | 15 | [tc-opus-G722-higher] 16 | source = opus 17 | destination = G722 18 | preference = 5 19 | 20 | # examples from docs: 21 | [tc-PCMA-PCMU-higher] 22 | source = PCMA 23 | destination = PCMU 24 | preference = 5 25 | 26 | [tc-GSM-G723-lower] 27 | source = G723 28 | destination = GSM 29 | preference = -5 30 | -------------------------------------------------------------------------------- /t/time-fudge-preload.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static inline long long timeval_us(const struct timeval *t) { 8 | return (long long) ((long long) t->tv_sec * 1000000LL) + t->tv_usec; 9 | } 10 | static inline void timeval_from_us(struct timeval *t, long long us) { 11 | t->tv_sec = us/1000000LL; 12 | t->tv_usec = us%1000000LL; 13 | } 14 | static inline long long timespec_ns(const struct timespec *t) { 15 | return (long long) ((long long) t->tv_sec * 1000000000LL) + t->tv_nsec; 16 | } 17 | static inline void timespec_from_ns(struct timespec *t, long long ns) { 18 | t->tv_sec = ns/1000000000LL; 19 | t->tv_nsec = ns%1000000000LL; 20 | } 21 | static long long offset = 0; 22 | int gettimeofday(struct timeval *restrict tv, void *restrict tz) { 23 | __typeof__ (gettimeofday) *fn = dlsym(RTLD_NEXT, "gettimeofday"); 24 | int ret = fn(tv, tz); 25 | if (ret) 26 | return ret; 27 | long r = random(); 28 | if (r < ((1L<<31) / 100)) { 29 | // 1% chance 30 | long long add = random() & 0xffff; 31 | offset += add; 32 | fprintf(stderr, "moving clock forward by %lli us\n", add); 33 | } 34 | long long tvs = timeval_us(tv); 35 | tvs += offset; 36 | timeval_from_us(tv, tvs); 37 | return ret; 38 | } 39 | int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) { 40 | __typeof__ (pthread_cond_timedwait) *fn = dlsym(RTLD_NEXT, "pthread_cond_timedwait"); 41 | if (!abstime) 42 | return fn(cond, mutex, abstime); 43 | struct timespec tn; 44 | long long ns = timespec_ns(abstime); 45 | ns -= offset * 1000LL; 46 | timespec_from_ns(&tn, ns); 47 | return fn(cond, mutex, &tn); 48 | } 49 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | sdp-parse-test 2 | kernel-module-test 3 | -------------------------------------------------------------------------------- /tests/simulator-tcp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # # ulimit -n 100000 3 | # # G_SLICE=always-malloc valgrind --leak-check=full --track-origins=yes --show-possibly-lost=yes ./rtpengine -t 0 -i $IP -l 25060 -f 4 | 5 | pipe_o() { 6 | nc localhost 25060 7 | } 8 | pipe() { 9 | pipe_o > /dev/null 10 | } 11 | ip() { 12 | echo $((RANDOM % 254 + 1)).$((RANDOM % 254 + 1)).$((RANDOM % 254 + 1)).$((RANDOM % 254 + 1)) 13 | } 14 | port() { 15 | echo $((RANDOM % 64000 + 1024)) 16 | } 17 | 18 | ids="" 19 | ports="" 20 | for (( i = 0 ; i < 1000 ; i++ )); do 21 | callid=$(uuid) 22 | test -z "$callid" && exit 1 23 | src=$(ip):$(port) 24 | dst=$(ip):$(port) 25 | gw=$(ip) 26 | fromtag=$(uuid) 27 | totag=$(uuid) 28 | 29 | src_rel=$(echo "request $callid $src:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o) 30 | dst_rel=$(echo "lookup $callid $dst:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:$totag,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o) 31 | echo "lookup $callid $dst:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:$totag,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe 32 | echo version | pipe 33 | (echo status | pipe) & 34 | 35 | src_path=${src_rel/ //} 36 | dst_path=${dst_rel/ //} 37 | ports="$ports $src_path $dst_path" 38 | for port in $ports; do 39 | echo foobar > "/dev/udp/$port" 40 | done 41 | 42 | ids="$ids $callid" 43 | done 44 | 45 | sleep 10 46 | 47 | for id in $ids; do 48 | echo "delete $id info=" | pipe 49 | done 50 | -------------------------------------------------------------------------------- /utils/.gitignore: -------------------------------------------------------------------------------- 1 | kplay-test 2 | -------------------------------------------------------------------------------- /utils/build_test_wrapper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | TARGET=$1 3 | ROOT=$(echo "$TARGET" | sed 's/\.h$//') 4 | if test "$MAKE" = ""; then 5 | # shellcheck disable=SC2209 6 | MAKE=make 7 | fi 8 | echo "Looking for usable alternative for $TARGET" 9 | rm -f "$ROOT"-test "$ROOT"-test.c 10 | ln -s ../lib/"$ROOT"-test.c . 11 | for x in ../lib/"$ROOT"*.h; do 12 | echo "Trying build with $x" 13 | rm -f "$TARGET" 14 | (echo '/******** GENERATED FILE ********/'; cat "$x") > "$TARGET" 15 | $MAKE "$ROOT"-test && break 16 | echo "Failed build with $x" 17 | rm -f "$TARGET" 18 | done 19 | rm -f "$ROOT"-test "$ROOT"-test.c 20 | test -f "$TARGET" 21 | -------------------------------------------------------------------------------- /utils/gen-bcg729-flags: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | have_bcg729="no" 4 | 5 | # look for bcg729 6 | if pkg-config --exists libbcg729; then 7 | # system pkg-config 8 | have_bcg729="yes" 9 | 10 | bcg729_inc="$(pkg-config --cflags libbcg729)" 11 | bcg729_lib="$(pkg-config --libs libbcg729)" 12 | elif [ -e /usr/include/bcg729/decoder.h ]; then 13 | # system generic 14 | have_bcg729="yes" 15 | bcg729_lib="-lbcg729" 16 | elif [ -e /usr/src/bcg729/include/bcg729/decoder.h ]; then 17 | # /usr/src 18 | have_bcg729=yes 19 | bcg729_inc="-I/usr/src/bcg729/include/" 20 | bcg729_lib="-L/usr/src/bcg729/src/ -lbcg729" 21 | elif [ -e "${HOME}/src/bcg729/include/bcg729/decoder.h" ]; then 22 | # rfuchs dev 23 | have_bcg729="yes" 24 | bcg729_inc="-I${HOME}/src/bcg729/include/" 25 | bcg729_lib="-L${HOME}/src/bcg729/src/ -lbcg729" 26 | elif [ -e "${HOME}/bcg729/include/bcg729/decoder.h" ]; then 27 | # home directory 28 | have_bcg729="yes" 29 | bcg729_inc="-I$(HOME)/bcg729/include/" 30 | bcg729_lib="-L$(HOME)/bcg729/src/ -lbcg729" 31 | elif [ -e "../bcg729/include/bcg729/decoder.h" ]; then 32 | # included toplevel 33 | have_bcg729="yes" 34 | bcg729_inc="-I../bcg729/include/" 35 | bcg729_lib="-L../bcg729/src/ -lbcg729" 36 | elif [ -e /usr/local/include/bcg729/decoder.h ]; then 37 | # /usr/local/include when installing from git 38 | have_bcg729="yes" 39 | bcg729_inc="-I/usr/local/include/" 40 | bcg729_lib="-L/usr/local/lib64/ -lbcg729" 41 | fi 42 | 43 | if [ "${have_bcg729}" = "yes" ]; then 44 | echo "CFLAGS_BCG729 := -DHAVE_BCG729" 45 | echo "CFLAGS_BCG729 += ${bcg729_inc}" 46 | echo "LDLIBS_BCG729 := ${bcg729_lib}" 47 | fi 48 | -------------------------------------------------------------------------------- /utils/gen-codec-chain-flags: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -e "${CODEC_CHAIN_HOME}/usr/include/codec-chain/client.h" ]; then 4 | echo "CFLAGS_CODEC_CHAIN := -DHAVE_CODEC_CHAIN -I${CODEC_CHAIN_HOME}/usr/include" 5 | elif [ -e /usr/include/codec-chain/client.h ]; then 6 | echo "CFLAGS_CODEC_CHAIN := -DHAVE_CODEC_CHAIN" 7 | fi 8 | -------------------------------------------------------------------------------- /utils/rtpengine-ctl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use IO::Socket::IP; 7 | use Getopt::Long; 8 | use Config::Tiny; 9 | 10 | my $ip; 11 | my $port; 12 | my $conffile = '/etc/rtpengine/rtpengine.conf'; 13 | my $listen; 14 | my $help; 15 | 16 | Getopt::Long::Configure('require_order'); 17 | 18 | my $optret = GetOptions( 19 | 'help|h' => \$help, 20 | 'ip=s' => \$ip, 21 | 'port=i' => \$port, 22 | 'config-file=s' => \$conffile, 23 | ); 24 | 25 | if (-f $conffile) { 26 | my $config = Config::Tiny->read($conffile); 27 | $config or die "Failed to read config file: " . Config::Tiny->errstr; 28 | 29 | $listen = $config->{rtpengine}{'listen-cli'} 30 | if $config->{rtpengine}; 31 | if ($listen =~ /^\d+$/) { 32 | $port //= $listen; 33 | } 34 | else { 35 | $ip //= $listen; 36 | } 37 | } 38 | 39 | if ($ip && $ip =~ s/:(\d+)$// && !$port) { 40 | $port = $1; 41 | } 42 | 43 | my $argumentstring = "@ARGV"; 44 | $argumentstring = trim($argumentstring); 45 | 46 | $ip //= '127.0.0.1'; 47 | $port //= 9900; 48 | 49 | if ($help || !$argumentstring || !$optret || $port <= 0 || $port > 65535) { 50 | $argumentstring = 'usage'; 51 | 52 | print "\n"; 53 | print " rtpengine-ctl [ -ip [:] -port ] \n"; 54 | print "\n"; 55 | } 56 | 57 | # create a connecting socket 58 | my $socket = IO::Socket::IP->new( 59 | PeerHost => $ip, 60 | PeerPort => $port, 61 | Proto => 'tcp', 62 | ); 63 | die "Cannot connect to rtpengine $!\n" unless $socket; 64 | 65 | $socket->autoflush(1); 66 | 67 | #set send/recv timeout so script doesn't hang when rtpengine doesn't interact 68 | setsockopt($socket, SOL_SOCKET, SO_SNDTIMEO, pack('L!L!', 3, 0) ) or die $!; 69 | setsockopt($socket, SOL_SOCKET, SO_RCVTIMEO, pack('L!L!', 3, 0) ) or die $!; 70 | 71 | my $size = $socket->send("$argumentstring\n"); 72 | 73 | # receive a response of up to 10MB 74 | my $response = ""; 75 | 76 | do { 77 | $response = ""; 78 | $socket->recv($response, 1024*1024*10); 79 | print $response; 80 | } while ( not $response eq ""); 81 | 82 | $socket->close(); 83 | 84 | sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s }; 85 | -------------------------------------------------------------------------------- /utils/rtpengine-ctl.1: -------------------------------------------------------------------------------- 1 | .TH man 1 "17 May 2022" "1.0" "rtpengine-ctl man page" 2 | .SH NAME 3 | rtpengine\-ctl \- utility for sending commands to ngcp\-rtpengine\-daemon service 4 | .SH SYNOPSIS 5 | rtpengine\-ctl [ -h -config-file -ip [:] -port ] 6 | .SH DESCRIPTION 7 | rtpengine\-ctl will send commands to the specified ngcp\-rtpengine\-daemon 8 | .SH OPTIONS 9 | .IP -h -help 10 | Will print a full list of supported commands. 11 | .IP -config-file 12 | Path to rtpengine.conf file. It will use ip:port defined there to connect to the ngcp\-rtpengine\-daemon service. 13 | .IP -ip 14 | ipaddress or ipaddress:port to use to connect to the ngcp\-rtpengine\-daemon. Defaults to 127.0.0.1 15 | .IP -port 16 | port to use to connect to the ngcp\-rtpengine\-daemon. Defaults to 9900 17 | .SH SEE ALSO 18 | rtpengine(8) 19 | .SH BUGS 20 | No known bugs. 21 | .SH AUTHOR 22 | Victor Seva (vseva@sipwise.com) 23 | -------------------------------------------------------------------------------- /utils/rtpengine-get-table: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Getopt::Long; 7 | use Config::Tiny; 8 | 9 | sub showusage { 10 | print <... 12 | 13 | Options: 14 | -h, --help print this help message. 15 | --config-file= use as the config file. 16 | --config-section= use
instead of 'rtpengine'. 17 | --fallback-table= use as fallback table instead of 0. 18 | HELP 19 | } 20 | 21 | my %options = ( 22 | 'help' => sub { showusage(); exit 0 }, 23 | 'config-file' => '/etc/rtpengine/rtpengine.conf', 24 | 'config-section' => 'rtpengine', 25 | 'fallback-table' => -1, 26 | ); 27 | 28 | Getopt::Long::Configure('require_order'); 29 | 30 | my $optret = GetOptions(\%options, 31 | 'help|h', 32 | 'config-file=s', 33 | 'config-section=s', 34 | 'fallback-table=i', 35 | ); 36 | 37 | if (!$optret) { 38 | showusage(); 39 | exit 1; 40 | } 41 | 42 | my $cfg = Config::Tiny->read($options{'config-file'}); 43 | if (not defined $cfg) { 44 | die "error: " . Config::Tiny->errstr . "\n"; 45 | } 46 | my $cfg_section = $options{'config-section'}; 47 | my $table = $cfg->{$cfg_section}{table} // $options{'fallback-table'}; 48 | 49 | print "$table\n"; 50 | 51 | 1; 52 | -------------------------------------------------------------------------------- /utils/rtpengine-ng-client.1: -------------------------------------------------------------------------------- 1 | .TH man 1 "17 May 2022" "1.0" "rtpengine-ng-client man page" 2 | .SH NAME 3 | rtpengine\-ng\-client \- utility for sending client commands to ngcp\-rtpengine\-daemon service 4 | .SH SYNOPSIS 5 | rtpengine\-ng\-client 6 | .SH DESCRIPTION 7 | rtpengine\-ng\-client will send NG commands to the specified ngcp\-rtpengine\-daemon service. 8 | This util is mainly used to simulate a client in a development environment. 9 | .SH OPTIONS 10 | See the full list of NG parameters at README. 11 | .SH SEE ALSO 12 | rtpengine(8) 13 | .SH BUGS 14 | No known bugs. 15 | .SH AUTHOR 16 | Victor Seva (vseva@sipwise.com) 17 | -------------------------------------------------------------------------------- /utils/srtcp-debug-helper: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use MIME::Base64; 6 | use NGCP::Rtpclient::SRTP; 7 | 8 | my $cs = $NGCP::Rtpclient::SRTP::crypto_suites{$ARGV[0]} or die; 9 | my $inline_key = $ARGV[1] or die; 10 | my ($key, $salt) = NGCP::Rtpclient::SRTP::decode_inline_base64($inline_key, $cs); 11 | my ($skey, $sauth, $ssalt) = NGCP::Rtpclient::SRTP::gen_rtcp_session_keys($key, $salt); 12 | print("Master key: " . unpack("H*", $key) . "\n"); 13 | print("Master salt: " . unpack("H*", $salt) . "\n"); 14 | print("RTCP session key: " . unpack("H*", $skey) . "\n"); 15 | print("RTCP session auth key: " . unpack("H*", $sauth) . "\n"); 16 | print("RTCP session salt: " . unpack("H*", $ssalt) . "\n"); 17 | 18 | my $pack = $ARGV[2]; 19 | my @pack; 20 | if ($pack =~ /:/) { 21 | my @pack = split(/:/, $pack); 22 | $pack = join('', (map {chr(hex($_))} @pack)); 23 | } 24 | else { 25 | $pack = pack("H*", $pack); 26 | } 27 | 28 | print("Packet length: " . length($pack) . " bytes\n"); 29 | 30 | my ($dec, $idx, $tag, $hmac) = NGCP::Rtpclient::SRTP::decrypt_rtcp($cs, $skey, $ssalt, $sauth, $pack); 31 | 32 | print("Auth tag from packet: " . unpack("H*", $tag) . "\n"); 33 | print("Computed auth tag: " . unpack("H*", $hmac) . "\n"); 34 | print("Decoded packet: " . unpack("H*", $dec) . "\n"); 35 | print("Index: $idx\n"); 36 | 37 | 38 | -------------------------------------------------------------------------------- /utils/srtp-debug-helper: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use MIME::Base64; 6 | use NGCP::Rtpclient::SRTP; 7 | 8 | my $cs = $NGCP::Rtpclient::SRTP::crypto_suites{$ARGV[0]} or die; 9 | my $inline_key = $ARGV[1] or die; 10 | my ($key, $salt) = NGCP::Rtpclient::SRTP::decode_inline_base64($inline_key, $cs); 11 | my ($skey, $sauth, $ssalt) = NGCP::Rtpclient::SRTP::gen_rtp_session_keys($key, $salt); 12 | print("Master key: " . unpack("H*", $key) . "\n"); 13 | print("Master salt: " . unpack("H*", $salt) . "\n"); 14 | print("RTP session key: " . unpack("H*", $skey) . "\n"); 15 | print("RTP session auth key: " . unpack("H*", $sauth) . "\n"); 16 | print("RTP session salt: " . unpack("H*", $ssalt) . "\n"); 17 | 18 | my $pack = $ARGV[2]; 19 | my @pack; 20 | if ($pack =~ /:/) { 21 | my @pack = split(/:/, $pack); 22 | $pack = join('', (map {chr(hex($_))} @pack)); 23 | } 24 | else { 25 | $pack = pack("H*", $pack); 26 | } 27 | 28 | my $in_roc = $ARGV[3] // 0; 29 | 30 | print("Packet length: " . length($pack) . " bytes\n"); 31 | 32 | my ($dec, $out_roc, $tag, $hmac) = NGCP::Rtpclient::SRTP::decrypt_rtp($cs, $skey, $ssalt, $sauth, $in_roc, $pack); 33 | 34 | print("Auth tag from packet: " . unpack("H*", $tag) . "\n"); 35 | print("Computed auth tag: " . unpack("H*", $hmac) . "\n"); 36 | print("Decoded packet: " . unpack("H*", $dec) . "\n"); 37 | print("ROC: $out_roc\n"); 38 | 39 | 40 | --------------------------------------------------------------------------------