├── .MODULE_NAME_librtp ├── .MODULE_LICENSE_BSD ├── README.md ├── atom.mk ├── .clang-format ├── COPYING ├── include └── rtp │ ├── rtp_jitter.h │ ├── rtp.h │ ├── ntp.h │ ├── rtp_pkt.h │ └── rtcp_pkt.h ├── tests └── test_rtp_ntp.c └── src ├── rtp_priv.h ├── rtp_pkt.c ├── rtp_jitter.c └── rtcp_pkt.c /.MODULE_NAME_librtp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.MODULE_LICENSE_BSD: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Parrot Drones SAS 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # librtp - RTP (Real-time Transport Protocol) library 2 | 3 | librtp is a C library to handle parsing and generating RTP/RTCP packets 4 | (see RFC3550). 5 | -------------------------------------------------------------------------------- /atom.mk: -------------------------------------------------------------------------------- 1 | 2 | LOCAL_PATH := $(call my-dir) 3 | 4 | include $(CLEAR_VARS) 5 | LOCAL_MODULE := librtp 6 | LOCAL_CATEGORY_PATH := libs 7 | LOCAL_DESCRIPTION := RTP (Real-time Transport Protocol) library 8 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include 9 | LOCAL_CFLAGS := -DRTP_API_EXPORTS -fvisibility=hidden -std=gnu99 10 | LOCAL_SRC_FILES := \ 11 | src/rtcp_pkt.c \ 12 | src/rtp_jitter.c \ 13 | src/rtp_pkt.c 14 | LOCAL_LIBRARIES := \ 15 | libfutils \ 16 | libpomp \ 17 | libulog 18 | 19 | ifeq ("$(TARGET_OS)","windows") 20 | LOCAL_LDLIBS += -lws2_32 21 | endif 22 | 23 | include $(BUILD_LIBRARY) 24 | 25 | ifdef TARGET_TEST 26 | include $(CLEAR_VARS) 27 | LOCAL_MODULE := tst-rtp 28 | LOCAL_CFLAGS := -std=gnu99 29 | LOCAL_SRC_FILES := tests/test_rtp_ntp.c 30 | LOCAL_LIBRARIES := librtp 31 | include $(BUILD_EXECUTABLE) 32 | endif 33 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | AccessModifierOffset: -8 4 | AlignAfterOpenBracket: true 5 | AlignEscapedNewlinesLeft: false 6 | AlignOperands: true 7 | AlignTrailingComments: false 8 | AllowAllParametersOfDeclarationOnNextLine: false 9 | AllowShortFunctionsOnASingleLine: Empty 10 | AllowShortIfStatementsOnASingleLine: false 11 | AllowShortLoopsOnASingleLine: false 12 | AlwaysBreakBeforeMultilineStrings: true 13 | BinPackArguments: false 14 | BinPackParameters: false 15 | BreakBeforeBinaryOperators: None 16 | BreakBeforeBraces: WebKit 17 | BreakConstructorInitializers: AfterColon 18 | BreakStringLiterals: false 19 | ContinuationIndentWidth: 8 20 | ConstructorInitializerIndentWidth: 16 21 | IndentCaseLabels: false 22 | IndentPPDirectives: AfterHash 23 | IndentWidth: 8 24 | Language: Cpp 25 | MaxEmptyLinesToKeep: 2 26 | SortIncludes: true 27 | SpaceAfterCStyleCast: false 28 | UseTab: Always 29 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Parrot Drones SAS 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | * Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright 8 | notice, this list of conditions and the following disclaimer in the 9 | documentation and/or other materials provided with the distribution. 10 | * Neither the name of the Parrot Drones SAS Company nor the 11 | names of its contributors may be used to endorse or promote products 12 | derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT DRONES SAS COMPANY BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /include/rtp/rtp_jitter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Parrot Drones SAS 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Parrot Drones SAS Company nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT DRONES SAS COMPANY BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _RTP_JITTER_H_ 28 | #define _RTP_JITTER_H_ 29 | 30 | 31 | struct rtp_pkt; 32 | struct rtp_jitter; 33 | 34 | 35 | struct rtp_jitter_cfg { 36 | uint32_t clk_rate; 37 | uint32_t delay; 38 | }; 39 | 40 | 41 | struct rtp_jitter_cbs { 42 | void (*process_pkt)(struct rtp_jitter *jitter, 43 | const struct rtp_pkt *pkt, 44 | uint32_t gap, 45 | void *userdata); 46 | }; 47 | 48 | 49 | RTP_API 50 | int rtp_jitter_new(const struct rtp_jitter_cfg *cfg, 51 | const struct rtp_jitter_cbs *cbs, 52 | void *userdata, 53 | struct rtp_jitter **ret_obj); 54 | 55 | 56 | RTP_API 57 | int rtp_jitter_destroy(struct rtp_jitter *self); 58 | 59 | 60 | RTP_API 61 | int rtp_jitter_clear(struct rtp_jitter *self, uint16_t next_seqnum); 62 | 63 | 64 | RTP_API 65 | int rtp_jitter_enqueue(struct rtp_jitter *self, struct rtp_pkt *pkt); 66 | 67 | 68 | RTP_API 69 | int rtp_jitter_process(struct rtp_jitter *self, uint64_t cur_timestamp); 70 | 71 | 72 | RTP_API 73 | int rtp_jitter_get_info(struct rtp_jitter *self, 74 | uint32_t *clk_rate, 75 | uint32_t *jitter_avg, 76 | int64_t *skew_avg); 77 | 78 | 79 | #endif /* _RTP_JITTER_H_ */ 80 | -------------------------------------------------------------------------------- /include/rtp/rtp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Parrot Drones SAS 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Parrot Drones SAS Company nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT DRONES SAS COMPANY BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _RTP_H_ 28 | #define _RTP_H_ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif /* __cplusplus */ 40 | 41 | /** To be used for all public API */ 42 | #ifdef RTP_API_EXPORTS 43 | # ifdef _WIN32 44 | # define RTP_API __declspec(dllexport) 45 | # else /* !_WIN32 */ 46 | # define RTP_API __attribute__((visibility("default"))) 47 | # endif /* !_WIN32 */ 48 | #else /* !RTP_API_EXPORTS */ 49 | # define RTP_API 50 | #endif /* !RTP_API_EXPORTS */ 51 | 52 | #include "rtp/ntp.h" 53 | #include "rtp/rtcp_pkt.h" 54 | #include "rtp/rtp_jitter.h" 55 | #include "rtp/rtp_pkt.h" 56 | 57 | 58 | static inline uint64_t rtp_timestamp_to_us(uint64_t rtp_timestamp, 59 | uint32_t clk_rate) 60 | { 61 | if (clk_rate == 0) 62 | return 0; 63 | else 64 | return (rtp_timestamp * 1000000 + clk_rate / 2) / clk_rate; 65 | } 66 | 67 | 68 | static inline uint64_t rtp_timestamp_from_us(uint64_t us, uint32_t clk_rate) 69 | { 70 | return (us * clk_rate + 500000) / 1000000; 71 | } 72 | 73 | 74 | #ifdef __cplusplus 75 | } 76 | #endif /* __cplusplus */ 77 | 78 | #endif /* !_RTP_H_ */ 79 | -------------------------------------------------------------------------------- /tests/test_rtp_ntp.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Parrot Drones SAS 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Parrot Drones SAS Company nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT DRONES SAS COMPANY BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "rtp/rtp.h" 33 | 34 | 35 | static void test_ntp_timestamp64(void) 36 | { 37 | int64_t r = 0; 38 | static const struct { 39 | struct ntp_timestamp64 t1; 40 | struct ntp_timestamp64 t2; 41 | int64_t r; 42 | } table[] = { 43 | {{1, 0x40000000}, {1, 0x20000000}, 125000}, 44 | {{1, 0x20000000}, {1, 0x40000000}, -125000}, 45 | {{5, 0x40000000}, {2, 0x20000000}, 3125000}, 46 | {{5, 0x20000000}, {2, 0x40000000}, 2875000}, 47 | {{2, 0x40000000}, {5, 0x20000000}, -2875000}, 48 | {{2, 0x20000000}, {5, 0x40000000}, -3125000}, 49 | }; 50 | 51 | for (uint32_t i = 0; i < sizeof(table) / sizeof(table[0]); i++) { 52 | ntp_timestamp64_diff_us(&table[i].t1, &table[i].t2, &r); 53 | printf("%" PRIi64 " %" PRIi64 "\n", r, table[i].r); 54 | } 55 | } 56 | 57 | 58 | static void test_ntp_timestamp32(void) 59 | { 60 | int64_t r = 0; 61 | static const struct { 62 | struct ntp_timestamp32 t1; 63 | struct ntp_timestamp32 t2; 64 | int64_t r; 65 | } table[] = { 66 | {{1, 0x4000}, {1, 0x2000}, 125000}, 67 | {{1, 0x2000}, {1, 0x4000}, -125000}, 68 | {{5, 0x4000}, {2, 0x2000}, 3125000}, 69 | {{5, 0x2000}, {2, 0x4000}, 2875000}, 70 | {{2, 0x4000}, {5, 0x2000}, -2875000}, 71 | {{2, 0x2000}, {5, 0x4000}, -3125000}, 72 | }; 73 | 74 | for (uint32_t i = 0; i < sizeof(table) / sizeof(table[0]); i++) { 75 | ntp_timestamp32_diff_us(&table[i].t1, &table[i].t2, &r); 76 | printf("%ld %ld\n", r, table[i].r); 77 | } 78 | } 79 | 80 | 81 | int main() 82 | { 83 | test_ntp_timestamp64(); 84 | test_ntp_timestamp32(); 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /src/rtp_priv.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Parrot Drones SAS 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Parrot Drones SAS Company nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT DRONES SAS COMPANY BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _RTP_PRIV_H_ 28 | #define _RTP_PRIV_H_ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #ifdef _WIN32 39 | # include 40 | #else /* !_WIN32 */ 41 | # include 42 | #endif /* !_WIN32 */ 43 | 44 | #define ULOG_TAG rtp 45 | #include 46 | 47 | #include 48 | #include 49 | 50 | #include "rtp/rtp.h" 51 | 52 | 53 | static inline int rtp_write_u8(struct pomp_buffer *buf, size_t *pos, uint8_t v) 54 | { 55 | return pomp_buffer_write(buf, pos, &v, sizeof(v)); 56 | } 57 | 58 | 59 | static inline int 60 | rtp_write_u16(struct pomp_buffer *buf, size_t *pos, uint16_t v) 61 | { 62 | v = htons(v); 63 | return pomp_buffer_write(buf, pos, &v, sizeof(v)); 64 | } 65 | 66 | 67 | static inline int 68 | rtp_write_u32(struct pomp_buffer *buf, size_t *pos, uint32_t v) 69 | { 70 | v = htonl(v); 71 | return pomp_buffer_write(buf, pos, &v, sizeof(v)); 72 | } 73 | 74 | 75 | static inline int 76 | rtp_write_u64(struct pomp_buffer *buf, size_t *pos, uint64_t v) 77 | { 78 | uint32_t _v[2]; 79 | _v[0] = htonl((v >> 32) & 0xffffffff); 80 | _v[1] = htonl(v & 0xffffffff); 81 | return pomp_buffer_write(buf, pos, _v, sizeof(_v)); 82 | } 83 | 84 | 85 | static inline int 86 | rtp_read_u8(const struct pomp_buffer *buf, size_t *pos, uint8_t *v) 87 | { 88 | return pomp_buffer_read(buf, pos, v, sizeof(*v)); 89 | } 90 | 91 | 92 | static inline int 93 | rtp_read_u16(const struct pomp_buffer *buf, size_t *pos, uint16_t *v) 94 | { 95 | int res = 0; 96 | res = pomp_buffer_read(buf, pos, v, sizeof(*v)); 97 | if (res == 0) 98 | *v = ntohs(*v); 99 | return res; 100 | } 101 | 102 | 103 | static inline int 104 | rtp_read_u32(const struct pomp_buffer *buf, size_t *pos, uint32_t *v) 105 | { 106 | int res = 0; 107 | res = pomp_buffer_read(buf, pos, v, sizeof(*v)); 108 | if (res == 0) 109 | *v = ntohl(*v); 110 | return res; 111 | } 112 | 113 | 114 | static inline int 115 | rtp_read_u64(const struct pomp_buffer *buf, size_t *pos, uint64_t *v) 116 | { 117 | int res = 0; 118 | uint32_t _v[2]; 119 | res = pomp_buffer_read(buf, pos, _v, sizeof(_v)); 120 | if (res == 0) 121 | *v = ((uint64_t)ntohl(_v[0]) << 32) | ntohl(_v[1]); 122 | return res; 123 | } 124 | 125 | 126 | static inline int16_t rtp_diff_seqnum(uint16_t sq1, uint16_t sq2) 127 | { 128 | return (int16_t)(sq1 - sq2); 129 | } 130 | 131 | 132 | #endif /* !_RTP_PRIV_H_ */ 133 | -------------------------------------------------------------------------------- /include/rtp/ntp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Parrot Drones SAS 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Parrot Drones SAS Company nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT DRONES SAS COMPANY BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _NTP_H_ 28 | #define _NTP_H_ 29 | 30 | 31 | struct ntp_timestamp64 { 32 | uint32_t seconds; 33 | uint32_t fraction; 34 | }; 35 | 36 | 37 | struct ntp_timestamp32 { 38 | uint16_t seconds; 39 | uint16_t fraction; 40 | }; 41 | 42 | 43 | static inline int ntp_timestamp64_diff_us(const struct ntp_timestamp64 *t1, 44 | const struct ntp_timestamp64 *t2, 45 | int64_t *d) 46 | { 47 | if (t1 == NULL || t2 == NULL || d == NULL) 48 | return -EINVAL; 49 | *d = ((int64_t)t1->seconds - (int64_t)t2->seconds) * 1000000; 50 | *d += (((int64_t)t1->fraction - (int64_t)t2->fraction) * 1000000) >> 32; 51 | return 0; 52 | } 53 | 54 | 55 | static inline int ntp_timestamp64_to_us(const struct ntp_timestamp64 *t, 56 | uint64_t *us) 57 | { 58 | if (t == NULL || us == NULL) 59 | return -EINVAL; 60 | *us = ((uint64_t)t->seconds * 1000000) + 61 | (((uint64_t)t->fraction * 1000000) >> 32); 62 | return 0; 63 | } 64 | 65 | 66 | static inline int ntp_timestamp64_from_us(struct ntp_timestamp64 *t, 67 | uint64_t us) 68 | { 69 | if (t == NULL) 70 | return -EINVAL; 71 | t->seconds = us / 1000000; 72 | t->fraction = ((uint64_t)(us % 1000000) << 32) / 1000000; 73 | return 0; 74 | } 75 | 76 | 77 | static inline int ntp_timestamp64_to_timespec(const struct ntp_timestamp64 *t, 78 | struct timespec *ts) 79 | { 80 | if (t == NULL || ts == NULL) 81 | return -EINVAL; 82 | ts->tv_sec = t->seconds; 83 | ts->tv_nsec = ((uint64_t)t->fraction * 1000000000) >> 32; 84 | return 0; 85 | } 86 | 87 | 88 | static inline int ntp_timestamp64_from_timespec(struct ntp_timestamp64 *t, 89 | const struct timespec *ts) 90 | { 91 | if (t == NULL || ts == NULL) 92 | return -EINVAL; 93 | t->seconds = ts->tv_sec; 94 | t->fraction = ((uint64_t)ts->tv_nsec << 32) / 1000000000; 95 | return 0; 96 | } 97 | 98 | 99 | static inline int 100 | ntp_timestamp64_to_ntp_timestamp32(const struct ntp_timestamp64 *t1, 101 | struct ntp_timestamp32 *t2) 102 | { 103 | if (t1 == NULL || t2 == NULL) 104 | return -EINVAL; 105 | t2->seconds = t1->seconds & 0xffff; 106 | t2->fraction = (t1->fraction >> 16) & 0xffff; 107 | return 0; 108 | } 109 | 110 | 111 | static inline int ntp_timestamp32_diff_us(const struct ntp_timestamp32 *t1, 112 | const struct ntp_timestamp32 *t2, 113 | int64_t *d) 114 | { 115 | if (t1 == NULL || t2 == NULL || d == NULL) 116 | return -EINVAL; 117 | *d = ((int64_t)t1->seconds - (int64_t)t2->seconds) * 1000000; 118 | *d += (((int64_t)t1->fraction - (int64_t)t2->fraction) * 1000000) >> 16; 119 | return 0; 120 | } 121 | 122 | 123 | static inline int ntp_timestamp32_to_us(const struct ntp_timestamp32 *t, 124 | uint64_t *us) 125 | { 126 | if (t == NULL || us == NULL) 127 | return -EINVAL; 128 | *us = ((uint64_t)t->seconds * 1000000) + 129 | (((uint64_t)t->fraction * 1000000) >> 16); 130 | return 0; 131 | } 132 | 133 | 134 | static inline int ntp_timestamp32_from_us(struct ntp_timestamp32 *t, 135 | uint64_t us) 136 | { 137 | if (t == NULL) 138 | return -EINVAL; 139 | t->seconds = us / 1000000; 140 | t->fraction = ((uint64_t)(us % 1000000) << 16) / 1000000; 141 | return 0; 142 | } 143 | 144 | 145 | static inline int ntp_timestamp32_to_timespec(const struct ntp_timestamp32 *t, 146 | struct timespec *ts) 147 | { 148 | if (t == NULL || ts == NULL) 149 | return -EINVAL; 150 | ts->tv_sec = t->seconds; 151 | ts->tv_nsec = ((uint64_t)t->fraction * 1000000000) >> 16; 152 | return 0; 153 | } 154 | 155 | 156 | static inline int ntp_timestamp32_from_timespec(struct ntp_timestamp32 *t, 157 | const struct timespec *ts) 158 | { 159 | if (t == NULL || ts == NULL) 160 | return -EINVAL; 161 | t->seconds = ts->tv_sec; 162 | t->fraction = ((uint64_t)ts->tv_nsec << 16) / 1000000000; 163 | return 0; 164 | } 165 | 166 | 167 | static inline int 168 | ntp_timestamp32_to_ntp_timestamp64(const struct ntp_timestamp32 *t1, 169 | struct ntp_timestamp64 *t2) 170 | { 171 | if (t1 == NULL || t2 == NULL) 172 | return -EINVAL; 173 | t2->seconds = t1->seconds; 174 | t2->fraction = t1->fraction << 16; 175 | return 0; 176 | } 177 | 178 | 179 | #endif /* !_NTP_H_ */ 180 | -------------------------------------------------------------------------------- /include/rtp/rtp_pkt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Parrot Drones SAS 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Parrot Drones SAS Company nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT DRONES SAS COMPANY BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _RTP_PKT_H_ 28 | #define _RTP_PKT_H_ 29 | 30 | 31 | #define RTP_PKT_VERSION 2 32 | 33 | #define RTP_PKT_HEADER_SIZE 12 34 | 35 | #define RTP_PKT_HEADER_FLAGS_VERSION_SHIFT 14 36 | #define RTP_PKT_HEADER_FLAGS_VERSION_MASK 0x0003 37 | 38 | #define RTP_PKT_HEADER_FLAGS_PADDING_SHIFT 13 39 | #define RTP_PKT_HEADER_FLAGS_PADDING_MASK 0x0001 40 | 41 | #define RTP_PKT_HEADER_FLAGS_EXTENSION_SHIFT 12 42 | #define RTP_PKT_HEADER_FLAGS_EXTENSION_MASK 0x0001 43 | 44 | #define RTP_PKT_HEADER_FLAGS_CSRC_SHIFT 8 45 | #define RTP_PKT_HEADER_FLAGS_CSRC_MASK 0x000f 46 | 47 | #define RTP_PKT_HEADER_FLAGS_MARKER_SHIFT 7 48 | #define RTP_PKT_HEADER_FLAGS_MARKER_MASK 0x0001 49 | 50 | #define RTP_PKT_HEADER_FLAGS_PAYLOAD_TYPE_SHIFT 0 51 | #define RTP_PKT_HEADER_FLAGS_PAYLOAD_TYPE_MASK 0x007f 52 | 53 | #define RTP_PKT_HEADER_FLAGS_GET(_flags, _name) \ 54 | (((_flags) >> RTP_PKT_HEADER_FLAGS_##_name##_SHIFT) & \ 55 | RTP_PKT_HEADER_FLAGS_##_name##_MASK) 56 | 57 | #define RTP_PKT_HEADER_FLAGS_SET(_flags, _name, _val) \ 58 | ((_flags) |= ((_val)&RTP_PKT_HEADER_FLAGS_##_name##_MASK) \ 59 | << RTP_PKT_HEADER_FLAGS_##_name##_SHIFT) 60 | 61 | 62 | /** 63 | * 5.1 RTP Fixed Header Fields 64 | * 65 | * 0 1 2 3 66 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 67 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 68 | * |V=2|P|X| CC |M| PT | sequence number | 69 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 70 | * | timestamp | 71 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 72 | * | synchronization source (SSRC) identifier | 73 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 74 | * | contributing source (CSRC) identifiers | 75 | * | .... | 76 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 77 | * 78 | * version (V): 2 bits 79 | * padding (P): 1 bit 80 | * extension (X): 1 bit 81 | * CSRC count (CC): 4 bits 82 | * marker (M): 1 bit 83 | * payload type (PT): 7 bits 84 | * sequence number: 16 bits 85 | * timestamp: 32 bits 86 | * SSRC: 32 bits 87 | * CSRC list: 0 to 15 items, 32 bits each 88 | * 89 | */ 90 | struct rtp_pkt_header { 91 | uint16_t flags; 92 | uint16_t seqnum; 93 | uint32_t timestamp; 94 | uint32_t ssrc; 95 | }; 96 | 97 | 98 | struct rtp_pkt { 99 | struct rtp_pkt_header header; 100 | 101 | struct { 102 | struct pomp_buffer *buf; 103 | const uint8_t *cdata; 104 | size_t len; 105 | } raw; 106 | 107 | struct { 108 | uint16_t id; 109 | size_t off; 110 | size_t len; 111 | } extheader; 112 | 113 | struct { 114 | size_t off; 115 | size_t len; 116 | } payload; 117 | 118 | struct { 119 | size_t off; 120 | size_t len; 121 | } padding; 122 | 123 | /* To be used in a list */ 124 | struct list_node node; 125 | 126 | /* Input timestamp (in us from monotonic clock) */ 127 | uint64_t in_timestamp; 128 | 129 | /* Output timestamp (in us from monotonic clock, unskewed) */ 130 | uint64_t out_timestamp; 131 | 132 | /* Extended RTP timestamp (without wrap) */ 133 | uint64_t rtp_timestamp; 134 | 135 | /** Priority of packet (inherited from the highest priority 136 | * of included NALUs (sender only) (low numbers have more priority) */ 137 | uint32_t priority; 138 | 139 | /** Importance of packet (inherited from the highest importance 140 | * of included NALUs (sender only) (low numbers are more important) */ 141 | uint32_t importance; 142 | 143 | /* Additional user data associated with this packet */ 144 | void *userdata; 145 | }; 146 | 147 | 148 | RTP_API 149 | int rtp_pkt_new(struct rtp_pkt **ret_obj); 150 | 151 | 152 | RTP_API 153 | int rtp_pkt_clone(const struct rtp_pkt *pkt, struct rtp_pkt **ret_obj); 154 | 155 | 156 | RTP_API 157 | int rtp_pkt_destroy(struct rtp_pkt *pkt); 158 | 159 | 160 | RTP_API 161 | int rtp_pkt_finalize_header(struct rtp_pkt *pkt); 162 | 163 | 164 | RTP_API 165 | int rtp_pkt_read(struct pomp_buffer *buf, struct rtp_pkt *pkt); 166 | 167 | 168 | #endif /* !_RTP_PKT_H_ */ 169 | -------------------------------------------------------------------------------- /src/rtp_pkt.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Parrot Drones SAS 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Parrot Drones SAS Company nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT DRONES SAS COMPANY BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include "rtp_priv.h" 28 | 29 | ULOG_DECLARE_TAG(rtp); 30 | 31 | /* clang-format off */ 32 | #define CHECK(_x) do { if ((res = (_x)) < 0) goto out; } while (0) 33 | /* clang-format on */ 34 | 35 | int rtp_pkt_new(struct rtp_pkt **ret_obj) 36 | { 37 | struct rtp_pkt *pkt = NULL; 38 | 39 | ULOG_ERRNO_RETURN_ERR_IF(ret_obj == NULL, EINVAL); 40 | 41 | *ret_obj = NULL; 42 | 43 | pkt = calloc(1, sizeof(*pkt)); 44 | if (pkt == NULL) 45 | return -ENOMEM; 46 | list_node_unref(&pkt->node); 47 | 48 | *ret_obj = pkt; 49 | return 0; 50 | } 51 | 52 | 53 | int rtp_pkt_clone(const struct rtp_pkt *pkt, struct rtp_pkt **ret_obj) 54 | { 55 | struct rtp_pkt *new_pkt = NULL; 56 | 57 | ULOG_ERRNO_RETURN_ERR_IF(ret_obj == NULL, EINVAL); 58 | 59 | *ret_obj = NULL; 60 | 61 | new_pkt = calloc(1, sizeof(*new_pkt)); 62 | if (new_pkt == NULL) 63 | return -ENOMEM; 64 | 65 | /* Copy contents, and add a ref on internal buffer */ 66 | *new_pkt = *pkt; 67 | list_node_unref(&new_pkt->node); 68 | if (pkt->raw.buf != NULL) 69 | pomp_buffer_ref(pkt->raw.buf); 70 | 71 | *ret_obj = new_pkt; 72 | return 0; 73 | } 74 | 75 | 76 | int rtp_pkt_destroy(struct rtp_pkt *pkt) 77 | { 78 | if (pkt == NULL) 79 | return 0; 80 | 81 | if (list_node_is_ref(&pkt->node)) 82 | ULOGW("packet %p is still in a list", pkt); 83 | if (pkt->raw.buf != NULL) 84 | pomp_buffer_unref(pkt->raw.buf); 85 | free(pkt); 86 | return 0; 87 | } 88 | 89 | 90 | static int rtp_pkt_write_header(struct pomp_buffer *buf, 91 | size_t *pos, 92 | const struct rtp_pkt_header *header) 93 | { 94 | int res = 0; 95 | CHECK(rtp_write_u16(buf, pos, header->flags)); 96 | CHECK(rtp_write_u16(buf, pos, header->seqnum)); 97 | CHECK(rtp_write_u32(buf, pos, header->timestamp)); 98 | CHECK(rtp_write_u32(buf, pos, header->ssrc)); 99 | out: 100 | return res; 101 | } 102 | 103 | 104 | int rtp_pkt_finalize_header(struct rtp_pkt *pkt) 105 | { 106 | int res = 0; 107 | size_t pos = 0; 108 | 109 | ULOG_ERRNO_RETURN_ERR_IF(pkt == NULL, EINVAL); 110 | ULOG_ERRNO_RETURN_ERR_IF(pkt->raw.buf == NULL, EINVAL); 111 | ULOG_ERRNO_RETURN_ERR_IF(pkt->raw.len < RTP_PKT_HEADER_SIZE, EINVAL); 112 | 113 | res = rtp_pkt_write_header(pkt->raw.buf, &pos, &pkt->header); 114 | 115 | return res; 116 | } 117 | 118 | 119 | static int rtp_pkt_read_header(const struct pomp_buffer *buf, 120 | size_t *pos, 121 | struct rtp_pkt_header *header) 122 | { 123 | int res = 0; 124 | CHECK(rtp_read_u16(buf, pos, &header->flags)); 125 | CHECK(rtp_read_u16(buf, pos, &header->seqnum)); 126 | CHECK(rtp_read_u32(buf, pos, &header->timestamp)); 127 | CHECK(rtp_read_u32(buf, pos, &header->ssrc)); 128 | out: 129 | return res; 130 | } 131 | 132 | 133 | int rtp_pkt_read(struct pomp_buffer *buf, struct rtp_pkt *pkt) 134 | { 135 | int res = 0; 136 | size_t pos = 0; 137 | uint32_t version = 0, csrc_count = 0; 138 | uint16_t u16 = 0; 139 | uint8_t padding = 0; 140 | 141 | ULOG_ERRNO_RETURN_ERR_IF(buf == NULL, EINVAL); 142 | ULOG_ERRNO_RETURN_ERR_IF(pkt == NULL, EINVAL); 143 | 144 | /* Get start/end of buffer */ 145 | pomp_buffer_ref(buf); 146 | pkt->raw.buf = buf; 147 | pomp_buffer_get_cdata( 148 | buf, (const void **)&pkt->raw.cdata, &pkt->raw.len, NULL); 149 | 150 | /* Read header */ 151 | if (pkt->raw.len < RTP_PKT_HEADER_SIZE) { 152 | res = -EIO; 153 | ULOGE("rtp: bad length: %zu (%u)", 154 | pkt->raw.len, 155 | RTP_PKT_HEADER_SIZE); 156 | goto out; 157 | } 158 | CHECK(rtp_pkt_read_header(buf, &pos, &pkt->header)); 159 | 160 | /* Check version */ 161 | version = RTP_PKT_HEADER_FLAGS_GET(pkt->header.flags, VERSION); 162 | if (version != RTP_PKT_VERSION) { 163 | res = -EIO; 164 | ULOGE("rtp: bad version: %u (%u)", version, RTP_PKT_VERSION); 165 | goto out; 166 | } 167 | 168 | /* Skip CSRC */ 169 | csrc_count = RTP_PKT_HEADER_FLAGS_GET(pkt->header.flags, CSRC); 170 | if (csrc_count > 0) { 171 | if (pkt->raw.len - pos < csrc_count * 4) { 172 | res = -EIO; 173 | ULOGE("rtp: bad length: %zu (%u)", 174 | pkt->raw.len - pos, 175 | csrc_count * 4); 176 | goto out; 177 | } 178 | pos += csrc_count * 4; 179 | } 180 | 181 | /* Do we have an extension header? */ 182 | if (RTP_PKT_HEADER_FLAGS_GET(pkt->header.flags, EXTENSION)) { 183 | if (pkt->raw.len - pos < 4) { 184 | res = -EIO; 185 | ULOGE("rtp: bad length: %zu (%u)", 186 | pkt->raw.len - pos, 187 | 4); 188 | goto out; 189 | } 190 | pkt->extheader.off = pos; 191 | 192 | /* Read extension id and length (number of 32-bit not 193 | * counting type + length itself) */ 194 | CHECK(rtp_read_u16(buf, &pos, &pkt->extheader.id)); 195 | CHECK(rtp_read_u16(buf, &pos, &u16)); 196 | pkt->extheader.len = u16 * 4 + 4; 197 | 198 | if (pkt->raw.len - pos < (size_t)u16 * 4) { 199 | res = -EIO; 200 | ULOGE("rtp: bad length: %zu (%u)", 201 | pkt->raw.len - pos, 202 | u16 * 4); 203 | goto out; 204 | } 205 | pos += u16 * 4; 206 | } 207 | 208 | /* Setup payload */ 209 | pkt->payload.off = pos; 210 | pkt->payload.len = pkt->raw.len - pos; 211 | 212 | /* Are there padding bytes? */ 213 | if (RTP_PKT_HEADER_FLAGS_GET(pkt->header.flags, PADDING)) { 214 | if (pkt->payload.len < 1) { 215 | res = -EIO; 216 | ULOGE("rtp: bad length: %zu (%u)", pkt->payload.len, 1); 217 | goto out; 218 | } 219 | pos = pkt->raw.len - 1; 220 | CHECK(rtp_read_u8(buf, &pos, &padding)); 221 | if (pkt->payload.len < padding) { 222 | res = -EIO; 223 | ULOGE("rtp: bad length: %zu (%u)", 224 | pkt->payload.len, 225 | padding); 226 | goto out; 227 | } 228 | pkt->payload.len -= padding; 229 | pkt->padding.off = pkt->payload.off + pkt->payload.len; 230 | pkt->padding.len = padding; 231 | } 232 | 233 | out: 234 | return res; 235 | } 236 | -------------------------------------------------------------------------------- /src/rtp_jitter.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Parrot Drones SAS 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Parrot Drones SAS Company nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT DRONES SAS COMPANY BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include "rtp_priv.h" 28 | 29 | #define SKEW_WINDOW_SLIDING 30 | 31 | #ifdef SKEW_WINDOW_SLIDING 32 | # define SKEW_WINDOW_MAX_SIZE 512 33 | # define SKEW_WINDOW_TIMEOUT 2000000 34 | # define SKEW_AVG_ALPHA 128 35 | #else 36 | # define SKEW_WINDOW_MAX_SIZE 400 37 | # define SKEW_WINDOW_TIMEOUT 5000000 38 | # define SKEW_AVG_ALPHA 64 39 | #endif 40 | 41 | #define SKEW_LARGE_GAP 1000000 42 | 43 | #define JITTER_AVG_ALPHA 16 44 | 45 | 46 | struct rtp_jitter { 47 | struct rtp_jitter_cfg cfg; 48 | struct rtp_jitter_cbs cbs; 49 | void *userdata; 50 | 51 | struct list_node packets; 52 | uint16_t next_seqnum; 53 | 54 | uint64_t first_rx_timestamp; 55 | uint64_t first_rtp_timestamp; 56 | 57 | uint64_t last_rx_timestamp; 58 | uint64_t last_rtp_timestamp; 59 | 60 | int64_t window[SKEW_WINDOW_MAX_SIZE]; 61 | uint32_t window_pos; 62 | uint32_t window_size; 63 | uint64_t window_start_timestamp; 64 | int64_t window_min; 65 | int64_t skew_avg; 66 | 67 | /* Estimated jitter (in us) */ 68 | uint32_t jitter_avg; 69 | }; 70 | 71 | 72 | static void reset_skew(struct rtp_jitter *self, 73 | uint64_t rx_timestamp, 74 | uint64_t rtp_timestamp) 75 | { 76 | self->first_rx_timestamp = rx_timestamp; 77 | self->first_rtp_timestamp = rtp_timestamp; 78 | self->window_pos = 0; 79 | self->window_size = 0; 80 | self->window_start_timestamp = 0; 81 | self->window_min = 0; 82 | self->skew_avg = 0; 83 | } 84 | 85 | 86 | /** 87 | * Interarrival jitter computation 88 | * J(i) = J(i-1) + (|D(i-1,i)| - J(i-1))/16 89 | * D(i,j) = (Rj - Ri) - (Sj - Si) = (Rj - Sj) - (Ri - Si) 90 | * Si is the RTP timestamp from packet i 91 | * Ri is the time of arrival in RTP timestamp units for packet i 92 | */ 93 | static void compute_jitter(struct rtp_jitter *self, 94 | uint64_t rx_timestamp, 95 | uint64_t rtp_timestamp) 96 | { 97 | uint32_t clk_rate = self->cfg.clk_rate; 98 | int64_t delta_rx = 0; 99 | int64_t delta_rtp = 0; 100 | int64_t jitter = 0; 101 | 102 | /* Compute delta in us */ 103 | delta_rx = rx_timestamp - self->last_rx_timestamp; 104 | delta_rtp = rtp_timestamp - self->last_rtp_timestamp; 105 | if (delta_rtp > 0) 106 | delta_rtp = rtp_timestamp_to_us(delta_rtp, clk_rate); 107 | else 108 | delta_rtp = -rtp_timestamp_to_us(-delta_rtp, clk_rate); 109 | 110 | jitter = delta_rx - delta_rtp; 111 | if (jitter < 0) 112 | jitter = -jitter; 113 | self->jitter_avg += (jitter - self->jitter_avg) / JITTER_AVG_ALPHA; 114 | } 115 | 116 | 117 | static uint64_t compute_skew(struct rtp_jitter *self, 118 | uint64_t rx_timestamp, 119 | uint64_t rtp_timestamp) 120 | { 121 | uint32_t clk_rate = self->cfg.clk_rate; 122 | int64_t delta_recv = 0; 123 | int64_t delta_send = 0; 124 | int64_t skew = 0; 125 | uint64_t out_timestamp = 0; 126 | 127 | /* Compute delta in us */ 128 | delta_send = rtp_timestamp - self->first_rtp_timestamp; 129 | if (delta_send < 0) { 130 | /* The sender probably restarted */ 131 | delta_send = -rtp_timestamp_to_us(-delta_send, clk_rate); 132 | ULOGD("reset skew: delta_send(%.6f) < 0", 133 | delta_send / 1000000.0); 134 | reset_skew(self, rx_timestamp, rtp_timestamp); 135 | delta_send = 0; 136 | } else { 137 | delta_send = rtp_timestamp_to_us(delta_send, clk_rate); 138 | } 139 | delta_recv = rx_timestamp - self->first_rx_timestamp; 140 | 141 | /* Current skew */ 142 | skew = delta_recv - delta_send; 143 | 144 | /* Check for large gaps */ 145 | if (skew - self->skew_avg < -SKEW_LARGE_GAP || 146 | skew - self->skew_avg > SKEW_LARGE_GAP) { 147 | ULOGD("reset skew: skew(%.6f) - skew_avg(%.6f) too large", 148 | skew / 1000000.0, 149 | self->skew_avg / 1000000.0); 150 | reset_skew(self, rx_timestamp, rtp_timestamp); 151 | delta_send = 0; 152 | delta_recv = 0; 153 | skew = 0; 154 | } 155 | 156 | #ifdef SKEW_WINDOW_SLIDING 157 | /* Are we at initialization stage? */ 158 | if (self->window_size == 0) { 159 | /* Save value */ 160 | self->window[self->window_pos] = skew; 161 | if (self->window_pos == 0) { 162 | /* First value in window */ 163 | self->window_start_timestamp = rx_timestamp; 164 | self->window_min = skew; 165 | } else if (skew < self->window_min) { 166 | /* New minimum found */ 167 | self->window_min = skew; 168 | } 169 | 170 | /* Are we done? */ 171 | self->window_pos++; 172 | if (self->window_pos >= SKEW_WINDOW_MAX_SIZE || 173 | rx_timestamp >= self->window_start_timestamp + 174 | SKEW_WINDOW_TIMEOUT) { 175 | self->window_size = self->window_pos; 176 | self->window_pos = 0; 177 | self->skew_avg = self->window_min; 178 | } else if (rx_timestamp >= self->window_start_timestamp) { 179 | uint32_t perc_time = 180 | (rx_timestamp - self->window_start_timestamp) * 181 | 100 / SKEW_WINDOW_TIMEOUT; 182 | uint32_t perc_window = 183 | self->window_pos * 100 / SKEW_WINDOW_MAX_SIZE; 184 | uint32_t perc = perc_time > perc_window ? perc_time 185 | : perc_window; 186 | /* Parabolic function */ 187 | perc = perc * perc; 188 | self->skew_avg += perc * 189 | (self->window_min - self->skew_avg) / 190 | 10000; 191 | } else { 192 | /* Might be different links */ 193 | ULOGD("reset skew: " 194 | "window_start_timestamp > rx_timestamp"); 195 | reset_skew(self, rx_timestamp, rtp_timestamp); 196 | out_timestamp = rx_timestamp; 197 | goto out; 198 | } 199 | } else { 200 | /* Remember the old value and set the new one */ 201 | int64_t old = self->window[self->window_pos]; 202 | self->window[self->window_pos] = skew; 203 | 204 | if (skew < self->window_min) { 205 | /* New minimum found */ 206 | self->window_min = skew; 207 | } else if (old == self->window_min) { 208 | /* We replace the current min value, find the new min */ 209 | self->window_min = INT64_MAX; 210 | for (uint32_t i = 0; i < self->window_size; i++) { 211 | if (self->window[i] == old) { 212 | /* We found the old min again, save it 213 | * and break */ 214 | self->window_min = self->window[i]; 215 | break; 216 | } else if (self->window[i] < self->window_min) { 217 | /* Save new min, and continue search */ 218 | self->window_min = self->window[i]; 219 | } 220 | } 221 | } 222 | 223 | /* Update position and wrap if needed */ 224 | self->window_pos++; 225 | if (self->window_pos >= self->window_size) 226 | self->window_pos = 0; 227 | 228 | /* Sliding average */ 229 | self->skew_avg += 230 | (self->window_min - self->skew_avg) / SKEW_AVG_ALPHA; 231 | } 232 | #else 233 | /* Fill the window */ 234 | if (self->window_size == 0) 235 | self->window_start_timestamp = rx_timestamp; 236 | self->window[self->window_size] = skew; 237 | self->window_size++; 238 | 239 | if ((self->window_size >= SKEW_WINDOW_MAX_SIZE) || 240 | (self->window_size >= SKEW_WINDOW_MAX_SIZE / 2 && 241 | rx_timestamp >= 242 | self->window_start_timestamp + SKEW_WINDOW_TIMEOUT)) { 243 | /* Window is full or half-full and on timeout */ 244 | self->window_min = self->window[0]; 245 | for (uint32_t i = 1; i < self->window_size; i++) { 246 | if (self->window[i] < self->window_min) 247 | self->window_min = self->window[i]; 248 | } 249 | 250 | /* Sliding average */ 251 | self->skew_avg += 252 | (self->window_min - self->skew_avg) / SKEW_AVG_ALPHA; 253 | 254 | /* Reset the window */ 255 | self->window_size = 0; 256 | } 257 | #endif 258 | 259 | /* Estimated out timestamp */ 260 | out_timestamp = self->first_rx_timestamp + delta_send + self->skew_avg; 261 | 262 | /* Make sure we don't go backwards */ 263 | if (out_timestamp + self->cfg.delay < rx_timestamp) { 264 | ULOGD("reset skew: out(%.6f) + delay(%.6f) < in(%.6f)", 265 | out_timestamp / 1000000.0, 266 | self->cfg.delay / 1000000.0, 267 | rx_timestamp / 1000000.0); 268 | reset_skew(self, rx_timestamp, rtp_timestamp); 269 | out_timestamp = rx_timestamp; 270 | } 271 | 272 | out: 273 | return out_timestamp; 274 | } 275 | 276 | 277 | int rtp_jitter_new(const struct rtp_jitter_cfg *cfg, 278 | const struct rtp_jitter_cbs *cbs, 279 | void *userdata, 280 | struct rtp_jitter **ret_obj) 281 | { 282 | struct rtp_jitter *self = NULL; 283 | 284 | ULOG_ERRNO_RETURN_ERR_IF(cfg == NULL, EINVAL); 285 | ULOG_ERRNO_RETURN_ERR_IF(cfg->clk_rate == 0, EINVAL); 286 | ULOG_ERRNO_RETURN_ERR_IF(cbs == NULL, EINVAL); 287 | ULOG_ERRNO_RETURN_ERR_IF(ret_obj == NULL, EINVAL); 288 | 289 | *ret_obj = NULL; 290 | 291 | self = calloc(1, sizeof(*self)); 292 | if (self == NULL) 293 | return -ENOMEM; 294 | self->cfg = *cfg; 295 | self->cbs = *cbs; 296 | self->userdata = userdata; 297 | list_init(&self->packets); 298 | 299 | *ret_obj = self; 300 | return 0; 301 | } 302 | 303 | 304 | int rtp_jitter_destroy(struct rtp_jitter *self) 305 | { 306 | if (self == NULL) 307 | return 0; 308 | 309 | rtp_jitter_clear(self, 0); 310 | free(self); 311 | return 0; 312 | } 313 | 314 | 315 | int rtp_jitter_clear(struct rtp_jitter *self, uint16_t next_seqnum) 316 | { 317 | struct rtp_pkt *pkt = NULL; 318 | 319 | ULOG_ERRNO_RETURN_ERR_IF(self == NULL, EINVAL); 320 | 321 | /* Destroy all packets in the queue */ 322 | while (!list_is_empty(&self->packets)) { 323 | pkt = list_entry( 324 | list_first(&self->packets), struct rtp_pkt, node); 325 | list_del(&pkt->node); 326 | rtp_pkt_destroy(pkt); 327 | } 328 | 329 | self->first_rx_timestamp = 0; 330 | self->first_rtp_timestamp = 0; 331 | self->last_rx_timestamp = 0; 332 | self->last_rtp_timestamp = 0; 333 | self->window_size = 0; 334 | self->window_start_timestamp = 0; 335 | self->skew_avg = 0; 336 | self->jitter_avg = 0; 337 | 338 | /* Set the seq num of the next expected packet */ 339 | self->next_seqnum = next_seqnum; 340 | return 0; 341 | } 342 | 343 | 344 | int rtp_jitter_enqueue(struct rtp_jitter *self, struct rtp_pkt *pkt) 345 | { 346 | uint64_t in_timestamp = 0; 347 | uint64_t rtp_timestamp = 0; 348 | struct rtp_pkt *item = NULL; 349 | 350 | ULOG_ERRNO_RETURN_ERR_IF(self == NULL, EINVAL); 351 | ULOG_ERRNO_RETURN_ERR_IF(pkt == NULL, EINVAL); 352 | 353 | in_timestamp = pkt->in_timestamp; 354 | rtp_timestamp = pkt->rtp_timestamp; 355 | 356 | if (self->first_rx_timestamp == 0 || self->first_rtp_timestamp == 0) 357 | reset_skew(self, in_timestamp, rtp_timestamp); 358 | 359 | if (self->last_rx_timestamp != 0 && self->last_rtp_timestamp != 0) 360 | compute_jitter(self, in_timestamp, rtp_timestamp); 361 | pkt->out_timestamp = compute_skew(self, in_timestamp, rtp_timestamp); 362 | 363 | self->last_rx_timestamp = in_timestamp; 364 | self->last_rtp_timestamp = rtp_timestamp; 365 | 366 | if (rtp_diff_seqnum(self->next_seqnum, pkt->header.seqnum) > 0) { 367 | /* Old packet or duplicate of a packet that has already 368 | * been processed */ 369 | rtp_pkt_destroy(pkt); 370 | return 0; 371 | } 372 | 373 | list_walk_entry_backward(&self->packets, item, node) 374 | { 375 | int16_t diff = rtp_diff_seqnum(item->header.seqnum, 376 | pkt->header.seqnum); 377 | if (diff > 0) 378 | continue; 379 | 380 | if (diff == 0) { 381 | /* Duplicate packet */ 382 | rtp_pkt_destroy(pkt); 383 | return 0; 384 | } 385 | 386 | /* Add in the list in order */ 387 | list_add_after(&item->node, &pkt->node); 388 | return 0; 389 | } 390 | 391 | /* Empty list or current packet to be added as first */ 392 | list_add_before(list_first(&self->packets), &pkt->node); 393 | return 0; 394 | } 395 | 396 | 397 | int rtp_jitter_process(struct rtp_jitter *self, uint64_t cur_timestamp) 398 | { 399 | struct rtp_pkt *pkt = NULL; 400 | uint32_t gap = 0; 401 | 402 | ULOG_ERRNO_RETURN_ERR_IF(self == NULL, EINVAL); 403 | 404 | while (!list_is_empty(&self->packets)) { 405 | /* Get first packet */ 406 | pkt = list_entry( 407 | list_first(&self->packets), struct rtp_pkt, node); 408 | 409 | /* Is it the next one to process? */ 410 | if (pkt->header.seqnum == self->next_seqnum) 411 | goto do_process; 412 | 413 | /* Is it time to process it? */ 414 | if (cur_timestamp >= pkt->out_timestamp + self->cfg.delay) 415 | goto do_process; 416 | 417 | /* No more packet eligible for process */ 418 | break; 419 | 420 | /* codecheck_ignore[INDENTED_LABEL] */ 421 | do_process: 422 | gap = rtp_diff_seqnum(pkt->header.seqnum, self->next_seqnum); 423 | (self->cbs.process_pkt)(self, pkt, gap, self->userdata); 424 | self->next_seqnum = (pkt->header.seqnum + 1) & 0xffff; 425 | list_del(&pkt->node); 426 | rtp_pkt_destroy(pkt); 427 | } 428 | 429 | return 0; 430 | } 431 | 432 | 433 | int rtp_jitter_get_info(struct rtp_jitter *self, 434 | uint32_t *clk_rate, 435 | uint32_t *jitter_avg, 436 | int64_t *skew_avg) 437 | { 438 | ULOG_ERRNO_RETURN_ERR_IF(self == NULL, EINVAL); 439 | 440 | if (clk_rate != NULL) 441 | *clk_rate = self->cfg.clk_rate; 442 | if (jitter_avg != NULL) 443 | *jitter_avg = self->jitter_avg; 444 | if (skew_avg != NULL) 445 | *skew_avg = self->skew_avg; 446 | 447 | return 0; 448 | } 449 | -------------------------------------------------------------------------------- /include/rtp/rtcp_pkt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Parrot Drones SAS 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Parrot Drones SAS Company nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT DRONES SAS COMPANY BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _RTCP_PKT_H_ 28 | #define _RTCP_PKT_H_ 29 | 30 | 31 | #define RTCP_PKT_VERSION 2 32 | 33 | #define RTCP_PKT_HEADER_SIZE 4 34 | 35 | #define RTCP_PKT_HEADER_FLAGS_VERSION_SHIFT 6 36 | #define RTCP_PKT_HEADER_FLAGS_VERSION_MASK 0x03 37 | 38 | #define RTCP_PKT_HEADER_FLAGS_PADDING_SHIFT 5 39 | #define RTCP_PKT_HEADER_FLAGS_PADDING_MASK 0x01 40 | 41 | #define RTCP_PKT_HEADER_FLAGS_COUNT_SHIFT 0 42 | #define RTCP_PKT_HEADER_FLAGS_COUNT_MASK 0x1f 43 | 44 | #define RTCP_PKT_HEADER_FLAGS(_name, _flags) \ 45 | (((_flags) >> RTCP_PKT_HEADER_FLAGS_##_name##_SHIFT) & \ 46 | RTCP_PKT_HEADER_FLAGS_##_name##_MASK) 47 | 48 | #define RTCP_PKT_TYPE_SR 200 49 | #define RTCP_PKT_TYPE_RR 201 50 | #define RTCP_PKT_TYPE_SDES 202 51 | #define RTCP_PKT_TYPE_BYE 203 52 | #define RTCP_PKT_TYPE_APP 204 53 | #define RTCP_PKT_TYPE_RTPFB 205 54 | 55 | #define RTCP_PKT_SDES_TYPE_END 0 56 | #define RTCP_PKT_SDES_TYPE_CNAME 1 57 | #define RTCP_PKT_SDES_TYPE_NAME 2 58 | #define RTCP_PKT_SDES_TYPE_EMAIL 3 59 | #define RTCP_PKT_SDES_TYPE_PHONE 4 60 | #define RTCP_PKT_SDES_TYPE_LOC 5 61 | #define RTCP_PKT_SDES_TYPE_TOOL 6 62 | #define RTCP_PKT_SDES_TYPE_NOTE 7 63 | #define RTCP_PKT_SDES_TYPE_PRIV 8 64 | 65 | /* 2000 pkts should be enough with a streaming at 9Mbits/s, 66 | * pkt average size of 500 and report every 100ms. */ 67 | #define RTPFB_MAX_PKT 2000 68 | 69 | 70 | /** 71 | * 0 1 2 3 72 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 73 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 74 | * header |V=2|P| ?? | PT | length | 75 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 76 | * 77 | * version (V): 2 bits 78 | * padding (P): 1 bit 79 | * packet type (PT): 8 bits 80 | * length: 16 bits 81 | */ 82 | struct rtcp_pkt_header { 83 | uint8_t flags; 84 | uint8_t type; 85 | uint16_t len; 86 | }; 87 | 88 | 89 | /** 90 | * 0 1 2 3 91 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 92 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 93 | * report | SSRC_1 (SSRC of first source) | 94 | * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 95 | * | fraction lost | cumulative number of packets lost | 96 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 97 | * | extended highest sequence number received | 98 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 99 | * | interarrival jitter | 100 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 101 | * | last SR (LSR) | 102 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 103 | * | delay since last SR (DLSR) | 104 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 105 | * 106 | * SSRC_n (source identifier): 32 bits 107 | * fraction lost: 8 bits 108 | * cumulative number of packets lost: 24 bits 109 | * extended highest sequence number received: 32 bits 110 | * interarrival jitter: 32 bits 111 | * last SR timestamp (LSR): 32 bits 112 | * delay since last SR (DLSR): 32 bits 113 | */ 114 | /* clang-format off */ 115 | struct rtcp_pkt_report_block { 116 | uint32_t ssrc; 117 | uint32_t fraction:8; 118 | int32_t lost:24; 119 | uint32_t ext_highest_seqnum; 120 | uint32_t jitter; 121 | struct ntp_timestamp32 lsr; 122 | uint32_t dlsr; 123 | }; 124 | /* clang-format on */ 125 | 126 | 127 | /** 128 | * 6.4.1 SR: Sender Report RTCP Packet 129 | * 130 | * 0 1 2 3 131 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 132 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 133 | * header |V=2|P| RC | PT=SR=200 | length | 134 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 135 | * | SSRC of sender | 136 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 137 | * sender | NTP timestamp, most significant word | 138 | * info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 139 | * | NTP timestamp, least significant word | 140 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 141 | * | RTP timestamp | 142 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 143 | * | sender's packet count | 144 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 145 | * | sender's octet count | 146 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 147 | * report | SSRC_1 (SSRC of first source) | 148 | * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 149 | * 1 | fraction lost | cumulative number of packets lost | 150 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 151 | * | extended highest sequence number received | 152 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 153 | * | interarrival jitter | 154 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 155 | * | last SR (LSR) | 156 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 157 | * | delay since last SR (DLSR) | 158 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 159 | * report | SSRC_2 (SSRC of second source) | 160 | * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 161 | * 2 : ... : 162 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 163 | * | profile-specific extensions | 164 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 165 | * 166 | * reception report count (RC): 5 bits 167 | * SSRC: 32 bits 168 | * NTP timestamp: 64 bits 169 | * RTP timestamp: 32 bits 170 | * sender's packet count: 32 bits 171 | * sender's octet count: 32 bits 172 | */ 173 | struct rtcp_pkt_sender_report { 174 | uint32_t ssrc; 175 | struct ntp_timestamp64 ntp_timestamp; 176 | uint32_t rtp_timestamp; 177 | uint32_t sender_packet_count; 178 | uint32_t sender_byte_count; 179 | 180 | uint32_t report_count; 181 | struct rtcp_pkt_report_block reports[31]; 182 | }; 183 | 184 | 185 | /** 186 | * 6.4.2 RR: Receiver Report RTCP Packet 187 | * 188 | * 0 1 2 3 189 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 190 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 191 | * header |V=2|P| RC | PT=RR=201 | length | 192 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 193 | * | SSRC of packet sender | 194 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 195 | * report | SSRC_1 (SSRC of first source) | 196 | * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 197 | * 1 | fraction lost | cumulative number of packets lost | 198 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 199 | * | extended highest sequence number received | 200 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 201 | * | interarrival jitter | 202 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 203 | * | last SR (LSR) | 204 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 205 | * | delay since last SR (DLSR) | 206 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 207 | * report | SSRC_2 (SSRC of second source) | 208 | * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 209 | * 2 : ... : 210 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 211 | * | profile-specific extensions | 212 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 213 | * 214 | * reception report count (RC): 5 bits 215 | * SSRC: 32 bits 216 | */ 217 | struct rtcp_pkt_receiver_report { 218 | uint32_t ssrc; 219 | 220 | uint32_t report_count; 221 | struct rtcp_pkt_report_block reports[31]; 222 | }; 223 | 224 | 225 | /** 226 | * RTPFB : Transport Feedback Report 227 | * 228 | * Chapter 3.1 229 | * https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01 230 | * 231 | * 0 1 2 3 232 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 233 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 234 | * |V=2|P| FMT=15 | PT=205 | length | 235 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 236 | * | SSRC of packet sender | 237 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 238 | * | SSRC of media source | 239 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 240 | * | base sequence number | packet status count | 241 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 242 | * | reference time | fb pkt. count | 243 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 244 | * | packet chunk | packet chunk | 245 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 246 | * : ... : 247 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 248 | * | packet chunk | recv delta | recv delta | 249 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 250 | * : ... : 251 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 252 | * | recv delta | recv delta | zero padding | 253 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 254 | * 255 | * Feedback Message Type: 5 bits 256 | * sender SSRC: 32 bits 257 | * media SSRC: 32 bits 258 | * base sequence number: 16 bits, sequence number of first packet reported 259 | * packet status count: 16 bits, number of packets reported 260 | * reference time: 24 bits absolute arbitrary reference for recv deltas (x64ms) 261 | * fb pkt. count: 8 bits counter of the feedback packet report 262 | * packet chunk: 16 bits list of packet status chunk in seq number order 263 | * recv delta: 8/16 bits packet reception time delta to reference time (x250us) 264 | */ 265 | struct rtcp_pkt_rtpfb_feedback { 266 | uint16_t seq_num; 267 | int16_t recv_delta; 268 | uint8_t pkt_status_symbol; 269 | }; 270 | 271 | struct rtcp_pkt_rtpfb_report { 272 | uint32_t sender_ssrc; 273 | uint32_t media_ssrc; 274 | uint16_t base_seq; 275 | uint16_t status_count; 276 | uint32_t ref_time; 277 | uint8_t feedback_pkt_count; 278 | 279 | /* Array length is feedback_pkt_count (max is RTPFB_MAX_PKT) */ 280 | struct rtcp_pkt_rtpfb_feedback *feedbacks; 281 | }; 282 | 283 | 284 | struct rtcp_pkt_sdes_item { 285 | uint8_t type; 286 | uint8_t data_len; 287 | const uint8_t *data; 288 | 289 | /* When type is RTCP_PKT_SDES_TYPE_PRIV */ 290 | struct { 291 | uint8_t prefix_len; 292 | const uint8_t *prefix; 293 | uint8_t value_len; 294 | const uint8_t *value; 295 | } priv; 296 | }; 297 | 298 | 299 | struct rtcp_pkt_sdes_chunk { 300 | uint32_t ssrc; 301 | 302 | uint32_t item_count; 303 | const struct rtcp_pkt_sdes_item *items; 304 | }; 305 | 306 | 307 | /** 308 | * 6.5 SDES: Source Description RTCP Packet 309 | * 310 | * 0 1 2 3 311 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 312 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 313 | * header |V=2|P| SC | PT=SDES=202 | length | 314 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 315 | * chunk | SSRC/CSRC_1 | 316 | * 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 317 | * | SDES items | 318 | * | ... | 319 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 320 | * chunk | SSRC/CSRC_2 | 321 | * 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 322 | * | SDES items | 323 | * | ... | 324 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 325 | * 326 | * source count (SC): 5 bits 327 | */ 328 | struct rtcp_pkt_sdes { 329 | uint32_t chunk_count; 330 | const struct rtcp_pkt_sdes_chunk *chunks; 331 | }; 332 | 333 | 334 | /** 335 | * 6.6 BYE: Goodbye RTCP Packet 336 | * 337 | * 0 1 2 3 338 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 339 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 340 | * |V=2|P| SC | PT=BYE=203 | length | 341 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 342 | * | SSRC/CSRC | 343 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 344 | * : ... : 345 | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 346 | * (opt) | length | reason for leaving ... 347 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 348 | * 349 | * source count (SC): 5 bits 350 | */ 351 | struct rtcp_pkt_bye { 352 | uint32_t source_count; 353 | uint32_t sources[31]; 354 | 355 | uint8_t reason_len; 356 | const uint8_t *reason; 357 | }; 358 | 359 | 360 | /** 361 | * 6.7 APP: Application-Defined RTCP Packet 362 | * 363 | * 0 1 2 3 364 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 365 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 366 | * |V=2|P| subtype | PT=APP=204 | length | 367 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 368 | * | SSRC/CSRC | 369 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 370 | * | name (ASCII) | 371 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 372 | * | application-dependent data ... 373 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 374 | * 375 | * subtype: 5 bits 376 | * name: 4 octets 377 | * application-dependent data: variable length 378 | */ 379 | struct rtcp_pkt_app { 380 | uint32_t ssrc; 381 | uint32_t name; 382 | 383 | uint8_t subtype; 384 | uint32_t data_len; 385 | const uint8_t *data; 386 | }; 387 | 388 | 389 | struct rtcp_pkt_read_cbs { 390 | void (*sender_report)(const struct rtcp_pkt_sender_report *sr, 391 | void *userdata); 392 | 393 | void (*receiver_report)(const struct rtcp_pkt_receiver_report *rr, 394 | void *userdata); 395 | 396 | void (*sdes_item)(uint32_t ssrc, 397 | const struct rtcp_pkt_sdes_item *item, 398 | void *userdata); 399 | 400 | void (*bye)(const struct rtcp_pkt_bye *bye, void *userdata); 401 | 402 | void (*app)(const struct rtcp_pkt_app *app, void *userdata); 403 | 404 | void (*rtpfb_report)(const struct rtcp_pkt_rtpfb_report *rtpfb, 405 | void *userdata); 406 | }; 407 | 408 | 409 | RTP_API 410 | const char *rtcp_pkt_type_str(uint8_t type); 411 | 412 | 413 | RTP_API 414 | const char *rtcp_pkt_sdes_type_str(uint8_t type); 415 | 416 | 417 | RTP_API 418 | int rtcp_pkt_write_sender_report(struct pomp_buffer *buf, 419 | size_t *pos, 420 | const struct rtcp_pkt_sender_report *sr); 421 | 422 | 423 | RTP_API 424 | int rtcp_pkt_write_receiver_report(struct pomp_buffer *buf, 425 | size_t *pos, 426 | const struct rtcp_pkt_receiver_report *rr); 427 | 428 | 429 | RTP_API 430 | int rtcp_pkt_write_sdes(struct pomp_buffer *buf, 431 | size_t *pos, 432 | const struct rtcp_pkt_sdes *sdes); 433 | 434 | 435 | RTP_API 436 | int rtcp_pkt_write_bye(struct pomp_buffer *buf, 437 | size_t *pos, 438 | const struct rtcp_pkt_bye *bye); 439 | 440 | 441 | RTP_API 442 | int rtcp_pkt_write_app(struct pomp_buffer *buf, 443 | size_t *pos, 444 | const struct rtcp_pkt_app *app); 445 | 446 | 447 | RTP_API 448 | int rtcp_pkt_write_rtpfb(struct pomp_buffer *buf, 449 | size_t *pos, 450 | const struct rtcp_pkt_rtpfb_report *cr); 451 | 452 | 453 | RTP_API 454 | int rtcp_pkt_read(const struct pomp_buffer *buf, 455 | const struct rtcp_pkt_read_cbs *cbs, 456 | void *userdata); 457 | 458 | 459 | #endif /* !_RTCP_PKT_H_ */ 460 | -------------------------------------------------------------------------------- /src/rtcp_pkt.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Parrot Drones SAS 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Parrot Drones SAS Company nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PARROT DRONES SAS COMPANY BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | /** 28 | * RFC 3550: RTP: A Transport Protocol for Real-Time Applications 29 | * 30 | * 6. RTP Control Protocol -- RTCP 31 | */ 32 | 33 | #include "rtp_priv.h" 34 | 35 | /* The max length in bits of the 'useful data' in a packet chunk. */ 36 | #define RUN_LENGTH_CHUNK_ACK_LG 13 37 | #define STATUS_VECTOR_CHUNK_ACK_LG 14 38 | #define STATUS_VECTOR_TWO_BIT_SYMBOLS_MASK 0x4000 39 | #define STATUS_VECTOR_CHUNK_MASK 0x8000 40 | 41 | /* clang-format off */ 42 | #define CHECK(_x) do { if ((res = (_x)) < 0) goto out; } while (0) 43 | /* clang-format on */ 44 | 45 | 46 | static int rtcp_pkt_write_header(struct pomp_buffer *buf, 47 | size_t *pos, 48 | const struct rtcp_pkt_header *header) 49 | { 50 | int res = 0; 51 | CHECK(rtp_write_u8(buf, pos, header->flags)); 52 | CHECK(rtp_write_u8(buf, pos, header->type)); 53 | CHECK(rtp_write_u16(buf, pos, header->len)); 54 | out: 55 | return res; 56 | } 57 | 58 | 59 | /** 60 | * 6.4 Sender and Receiver Reports 61 | * 6.4.1 SR: Sender Report RTCP Packet 62 | * 6.4.2 RR: Receiver Report RTCP Packet 63 | */ 64 | static int rtcp_pkt_write_report_block(struct pomp_buffer *buf, 65 | size_t *pos, 66 | const struct rtcp_pkt_report_block *rb) 67 | { 68 | int res = 0; 69 | uint32_t fraction_lost = (rb->fraction << 24) | (rb->lost & 0xffffff); 70 | CHECK(rtp_write_u32(buf, pos, rb->ssrc)); 71 | CHECK(rtp_write_u32(buf, pos, fraction_lost)); 72 | CHECK(rtp_write_u32(buf, pos, rb->ext_highest_seqnum)); 73 | CHECK(rtp_write_u32(buf, pos, rb->jitter)); 74 | CHECK(rtp_write_u16(buf, pos, rb->lsr.seconds)); 75 | CHECK(rtp_write_u16(buf, pos, rb->lsr.fraction)); 76 | CHECK(rtp_write_u32(buf, pos, rb->dlsr)); 77 | out: 78 | return res; 79 | } 80 | 81 | 82 | /** 83 | * 6.5 SDES: Source Description RTCP Packet 84 | * 6.5.8 PRIV: Private Extensions SDES Item 85 | */ 86 | static int rtcp_pkt_write_sdes_item(struct pomp_buffer *buf, 87 | size_t *pos, 88 | const struct rtcp_pkt_sdes_item *item) 89 | { 90 | int res = 0; 91 | uint32_t data_len = 0; 92 | 93 | CHECK(rtp_write_u8(buf, pos, item->type)); 94 | 95 | if (item->data != NULL && item->data_len > 0) { 96 | /* Normal item data */ 97 | CHECK(rtp_write_u8(buf, pos, item->data_len)); 98 | CHECK(pomp_buffer_write(buf, pos, item->data, item->data_len)); 99 | } else if (item->type == RTCP_PKT_SDES_TYPE_PRIV) { 100 | /* Private item data: prefix_len + prefix + value */ 101 | data_len = item->priv.prefix_len + item->priv.value_len + 1; 102 | if (data_len > 255) { 103 | res = -EINVAL; 104 | ULOGE("sdes: bad prefix/value length: %u/%u", 105 | item->priv.prefix_len, 106 | item->priv.value_len); 107 | goto out; 108 | } 109 | CHECK(rtp_write_u8(buf, pos, (uint8_t)data_len)); 110 | CHECK(rtp_write_u8(buf, pos, item->priv.prefix_len)); 111 | CHECK(pomp_buffer_write( 112 | buf, pos, item->priv.prefix, item->priv.prefix_len)); 113 | CHECK(pomp_buffer_write( 114 | buf, pos, item->priv.value, item->priv.value_len)); 115 | } else { 116 | /* No item data */ 117 | CHECK(rtp_write_u8(buf, pos, 0)); 118 | } 119 | 120 | out: 121 | return res; 122 | } 123 | 124 | 125 | /** 126 | * 6.5 SDES: Source Description RTCP Packet 127 | */ 128 | static int rtcp_pkt_write_sdes_chunk(struct pomp_buffer *buf, 129 | size_t *pos, 130 | const struct rtcp_pkt_sdes_chunk *chunk) 131 | { 132 | int res = 0; 133 | 134 | CHECK(rtp_write_u32(buf, pos, chunk->ssrc)); 135 | 136 | /* Write items */ 137 | for (uint32_t i = 0; i < chunk->item_count; i++) 138 | CHECK(rtcp_pkt_write_sdes_item(buf, pos, &chunk->items[i])); 139 | 140 | /* Add final null item and pad until aligned */ 141 | CHECK(rtp_write_u8(buf, pos, 0)); 142 | while (*pos % 4 != 0) 143 | CHECK(rtp_write_u8(buf, pos, 0)); 144 | 145 | out: 146 | return res; 147 | } 148 | 149 | 150 | /** 151 | * Get the RTCP Feedback packet chunks 'status'. 152 | * This means: 153 | * - symbol list in Status Vector Chunk 154 | * - run length in Run Length Chunk 155 | * Refer to google congestion control draft for more info: 156 | * draft-holmer-rmcat-transport-wide-cc-extensions-01.pdf 157 | */ 158 | static uint16_t 159 | build_rtcpfb_chunk_status(uint8_t *symbols, uint16_t len, bool two_bit_symbols) 160 | { 161 | const int bits_per_symbol = two_bit_symbols ? 2 : 1; 162 | const int nb_symbols = STATUS_VECTOR_CHUNK_ACK_LG / bits_per_symbol; 163 | 164 | uint16_t status = STATUS_VECTOR_CHUNK_MASK; 165 | if (two_bit_symbols) 166 | status |= STATUS_VECTOR_TWO_BIT_SYMBOLS_MASK; 167 | 168 | for (int i = 0; i < len; i++) { 169 | const int bit_pos = (nb_symbols - 1 - i) * bits_per_symbol; 170 | status |= symbols[i] << bit_pos; 171 | } 172 | 173 | return status; 174 | } 175 | 176 | 177 | const char *rtcp_pkt_type_str(uint8_t type) 178 | { 179 | switch (type) { 180 | case RTCP_PKT_TYPE_SR: 181 | return "RR"; 182 | case RTCP_PKT_TYPE_RR: 183 | return "SR"; 184 | case RTCP_PKT_TYPE_SDES: 185 | return "SDES"; 186 | case RTCP_PKT_TYPE_BYE: 187 | return "BYE"; 188 | case RTCP_PKT_TYPE_APP: 189 | return "APP"; 190 | case RTCP_PKT_TYPE_RTPFB: 191 | return "RTPFB"; 192 | default: 193 | return "UNKNOWN"; 194 | } 195 | } 196 | 197 | 198 | const char *rtcp_pkt_sdes_type_str(uint8_t type) 199 | { 200 | switch (type) { 201 | case RTCP_PKT_SDES_TYPE_END: 202 | return "END"; 203 | case RTCP_PKT_SDES_TYPE_CNAME: 204 | return "CNAME"; 205 | case RTCP_PKT_SDES_TYPE_NAME: 206 | return "NAME"; 207 | case RTCP_PKT_SDES_TYPE_EMAIL: 208 | return "EMAIL"; 209 | case RTCP_PKT_SDES_TYPE_PHONE: 210 | return "PHONE"; 211 | case RTCP_PKT_SDES_TYPE_LOC: 212 | return "LOC"; 213 | case RTCP_PKT_SDES_TYPE_TOOL: 214 | return "TOOL"; 215 | case RTCP_PKT_SDES_TYPE_NOTE: 216 | return "NOTE"; 217 | case RTCP_PKT_SDES_TYPE_PRIV: 218 | return "PRIV"; 219 | default: 220 | return "UNKNOWN"; 221 | } 222 | } 223 | 224 | 225 | /** 226 | * 6.4.1 SR: Sender Report RTCP Packet 227 | */ 228 | int rtcp_pkt_write_sender_report(struct pomp_buffer *buf, 229 | size_t *pos, 230 | const struct rtcp_pkt_sender_report *sr) 231 | { 232 | int res = 0; 233 | struct rtcp_pkt_header header; 234 | size_t header_pos = 0; 235 | 236 | ULOG_ERRNO_RETURN_ERR_IF(buf == NULL, EINVAL); 237 | ULOG_ERRNO_RETURN_ERR_IF(pos == NULL, EINVAL); 238 | ULOG_ERRNO_RETURN_ERR_IF(sr == NULL, EINVAL); 239 | 240 | /* Remember where to write header and skip it */ 241 | header_pos = *pos; 242 | *pos += RTCP_PKT_HEADER_SIZE; 243 | 244 | CHECK(rtp_write_u32(buf, pos, sr->ssrc)); 245 | CHECK(rtp_write_u32(buf, pos, sr->ntp_timestamp.seconds)); 246 | CHECK(rtp_write_u32(buf, pos, sr->ntp_timestamp.fraction)); 247 | CHECK(rtp_write_u32(buf, pos, sr->rtp_timestamp)); 248 | CHECK(rtp_write_u32(buf, pos, sr->sender_packet_count)); 249 | CHECK(rtp_write_u32(buf, pos, sr->sender_byte_count)); 250 | 251 | for (uint32_t i = 0; i < sr->report_count; i++) 252 | CHECK(rtcp_pkt_write_report_block(buf, pos, &sr->reports[i])); 253 | 254 | /* Write header */ 255 | memset(&header, 0, sizeof(header)); 256 | header.flags = 257 | (RTCP_PKT_VERSION << RTCP_PKT_HEADER_FLAGS_VERSION_SHIFT) | 258 | (sr->report_count << RTCP_PKT_HEADER_FLAGS_COUNT_SHIFT); 259 | header.type = RTCP_PKT_TYPE_SR; 260 | header.len = (*pos - header_pos) / 4 - 1; 261 | CHECK(rtcp_pkt_write_header(buf, &header_pos, &header)); 262 | 263 | out: 264 | return res; 265 | } 266 | 267 | 268 | /** 269 | * 6.4.2 RR: Receiver Report RTCP Packet 270 | */ 271 | int rtcp_pkt_write_receiver_report(struct pomp_buffer *buf, 272 | size_t *pos, 273 | const struct rtcp_pkt_receiver_report *rr) 274 | { 275 | int res = 0; 276 | struct rtcp_pkt_header header; 277 | size_t header_pos = 0; 278 | 279 | ULOG_ERRNO_RETURN_ERR_IF(buf == NULL, EINVAL); 280 | ULOG_ERRNO_RETURN_ERR_IF(pos == NULL, EINVAL); 281 | ULOG_ERRNO_RETURN_ERR_IF(rr == NULL, EINVAL); 282 | 283 | /* Remember where to write header and skip it */ 284 | header_pos = *pos; 285 | *pos += RTCP_PKT_HEADER_SIZE; 286 | 287 | CHECK(rtp_write_u32(buf, pos, rr->ssrc)); 288 | 289 | for (uint32_t i = 0; i < rr->report_count; i++) 290 | CHECK(rtcp_pkt_write_report_block(buf, pos, &rr->reports[i])); 291 | 292 | /* Write header */ 293 | memset(&header, 0, sizeof(header)); 294 | header.flags = 295 | (RTCP_PKT_VERSION << RTCP_PKT_HEADER_FLAGS_VERSION_SHIFT) | 296 | (rr->report_count << RTCP_PKT_HEADER_FLAGS_COUNT_SHIFT); 297 | header.type = RTCP_PKT_TYPE_RR; 298 | header.len = (*pos - header_pos) / 4 - 1; 299 | CHECK(rtcp_pkt_write_header(buf, &header_pos, &header)); 300 | 301 | out: 302 | return res; 303 | } 304 | 305 | 306 | /** 307 | * 6.5 SDES: Source Description RTCP Packet 308 | */ 309 | int rtcp_pkt_write_sdes(struct pomp_buffer *buf, 310 | size_t *pos, 311 | const struct rtcp_pkt_sdes *sdes) 312 | { 313 | int res = 0; 314 | struct rtcp_pkt_header header; 315 | size_t header_pos = 0; 316 | 317 | ULOG_ERRNO_RETURN_ERR_IF(buf == NULL, EINVAL); 318 | ULOG_ERRNO_RETURN_ERR_IF(pos == NULL, EINVAL); 319 | ULOG_ERRNO_RETURN_ERR_IF(sdes == NULL, EINVAL); 320 | 321 | /* Remember where to write header and skip it */ 322 | header_pos = *pos; 323 | *pos += RTCP_PKT_HEADER_SIZE; 324 | 325 | /* Write chunks */ 326 | for (uint32_t i = 0; i < sdes->chunk_count; i++) 327 | CHECK(rtcp_pkt_write_sdes_chunk(buf, pos, &sdes->chunks[i])); 328 | 329 | /* Write header */ 330 | memset(&header, 0, sizeof(header)); 331 | header.flags = 332 | (RTCP_PKT_VERSION << RTCP_PKT_HEADER_FLAGS_VERSION_SHIFT) | 333 | (sdes->chunk_count << RTCP_PKT_HEADER_FLAGS_COUNT_SHIFT); 334 | header.type = RTCP_PKT_TYPE_SDES; 335 | header.len = (*pos - header_pos) / 4 - 1; 336 | CHECK(rtcp_pkt_write_header(buf, &header_pos, &header)); 337 | 338 | out: 339 | return res; 340 | } 341 | 342 | 343 | /** 344 | * 6.6 BYE: Goodbye RTCP Packet 345 | */ 346 | int rtcp_pkt_write_bye(struct pomp_buffer *buf, 347 | size_t *pos, 348 | const struct rtcp_pkt_bye *bye) 349 | { 350 | int res = 0; 351 | struct rtcp_pkt_header header; 352 | size_t header_pos = 0; 353 | 354 | ULOG_ERRNO_RETURN_ERR_IF(buf == NULL, EINVAL); 355 | ULOG_ERRNO_RETURN_ERR_IF(pos == NULL, EINVAL); 356 | ULOG_ERRNO_RETURN_ERR_IF(bye == NULL, EINVAL); 357 | 358 | /* Remember where to write header and skip it */ 359 | header_pos = *pos; 360 | *pos += RTCP_PKT_HEADER_SIZE; 361 | 362 | /* Write sources */ 363 | for (uint32_t i = 0; i < bye->source_count; i++) 364 | CHECK(rtp_write_u32(buf, pos, bye->sources[i])); 365 | 366 | /* Write reason */ 367 | if (bye->reason != NULL && bye->reason_len > 0) { 368 | CHECK(rtp_write_u8(buf, pos, bye->reason_len)); 369 | CHECK(pomp_buffer_write( 370 | buf, pos, bye->reason, bye->reason_len)); 371 | /* Pad until aligned */ 372 | while (*pos % 4 != 0) 373 | CHECK(rtp_write_u8(buf, pos, 0)); 374 | } 375 | 376 | /* Write header */ 377 | memset(&header, 0, sizeof(header)); 378 | header.flags = 379 | (RTCP_PKT_VERSION << RTCP_PKT_HEADER_FLAGS_VERSION_SHIFT) | 380 | (bye->source_count << RTCP_PKT_HEADER_FLAGS_COUNT_SHIFT); 381 | header.type = RTCP_PKT_TYPE_BYE; 382 | header.len = (*pos - header_pos) / 4 - 1; 383 | CHECK(rtcp_pkt_write_header(buf, &header_pos, &header)); 384 | 385 | out: 386 | return res; 387 | } 388 | 389 | 390 | /** 391 | * 6.7 APP: Application-Defined RTCP Packet 392 | */ 393 | int rtcp_pkt_write_app(struct pomp_buffer *buf, 394 | size_t *pos, 395 | const struct rtcp_pkt_app *app) 396 | { 397 | int res = 0; 398 | struct rtcp_pkt_header header; 399 | size_t header_pos = 0; 400 | 401 | ULOG_ERRNO_RETURN_ERR_IF(buf == NULL, EINVAL); 402 | ULOG_ERRNO_RETURN_ERR_IF(pos == NULL, EINVAL); 403 | ULOG_ERRNO_RETURN_ERR_IF(app == NULL, EINVAL); 404 | 405 | /* Remember where to write header and skip it */ 406 | header_pos = *pos; 407 | *pos += RTCP_PKT_HEADER_SIZE; 408 | 409 | /* Write data */ 410 | CHECK(rtp_write_u32(buf, pos, app->ssrc)); 411 | CHECK(rtp_write_u32(buf, pos, app->name)); 412 | if (app->data != NULL && app->data_len > 0) 413 | CHECK(pomp_buffer_write(buf, pos, app->data, app->data_len)); 414 | 415 | /* Align on 32-bit */ 416 | while (*pos % 4 != 0) 417 | CHECK(rtp_write_u8(buf, pos, 0)); 418 | 419 | /* Write header */ 420 | memset(&header, 0, sizeof(header)); 421 | header.flags = 422 | (RTCP_PKT_VERSION << RTCP_PKT_HEADER_FLAGS_VERSION_SHIFT) | 423 | (app->subtype << RTCP_PKT_HEADER_FLAGS_COUNT_SHIFT); 424 | header.type = RTCP_PKT_TYPE_APP; 425 | header.len = (*pos - header_pos) / 4 - 1; 426 | CHECK(rtcp_pkt_write_header(buf, &header_pos, &header)); 427 | 428 | out: 429 | return res; 430 | } 431 | 432 | 433 | /** 434 | * RTPFB Congestion Control Feedback 435 | */ 436 | int rtcp_pkt_write_rtpfb(struct pomp_buffer *buf, 437 | size_t *pos, 438 | const struct rtcp_pkt_rtpfb_report *rtpfb) 439 | { 440 | int res = 0; 441 | struct rtcp_pkt_header header; 442 | size_t header_pos = 0; 443 | 444 | ULOG_ERRNO_RETURN_ERR_IF(buf == NULL, EINVAL); 445 | ULOG_ERRNO_RETURN_ERR_IF(pos == NULL, EINVAL); 446 | ULOG_ERRNO_RETURN_ERR_IF(rtpfb == NULL, EINVAL); 447 | 448 | /* Remember where to write header and skip it */ 449 | header_pos = *pos; 450 | *pos += RTCP_PKT_HEADER_SIZE; 451 | 452 | CHECK(rtp_write_u32(buf, pos, rtpfb->sender_ssrc)); 453 | CHECK(rtp_write_u32(buf, pos, rtpfb->media_ssrc)); 454 | CHECK(rtp_write_u16(buf, pos, rtpfb->base_seq)); 455 | CHECK(rtp_write_u16(buf, pos, rtpfb->status_count)); 456 | 457 | CHECK(rtp_write_u16(buf, pos, rtpfb->ref_time >> 8)); 458 | CHECK(rtp_write_u8(buf, pos, rtpfb->ref_time & 0xFF)); 459 | CHECK(rtp_write_u8(buf, pos, rtpfb->feedback_pkt_count)); 460 | 461 | /* Write packet chunks */ 462 | struct pkt_chunk { 463 | /* Represents the Run Length in a Run Length Chunk. 464 | * Represents Status symbol list in Status Vector Chunk. */ 465 | uint8_t ack[STATUS_VECTOR_CHUNK_ACK_LG]; 466 | /* Boolean value to indicate if it is 467 | * a run length chunk or a status chunk */ 468 | uint8_t run; 469 | /* Boolean value to indicate if it is a status with 470 | * only small deltas or also contains large deltas */ 471 | uint8_t large; 472 | uint16_t pos; 473 | }; 474 | 475 | uint16_t i, j; 476 | struct pkt_chunk chunk; 477 | chunk.run = 1; 478 | chunk.large = 0; 479 | chunk.pos = 0; 480 | for (i = 0; i < rtpfb->status_count; ++i) { 481 | /* Check if chunk should be written */ 482 | if (chunk.pos == STATUS_VECTOR_CHUNK_ACK_LG && 483 | chunk.large == 0 && chunk.run == 0) { 484 | /* Status Vector Chunk containing 14 1-bits symbols */ 485 | uint16_t status = build_rtcpfb_chunk_status( 486 | chunk.ack, chunk.pos, false); 487 | CHECK(rtp_write_u16(buf, pos, status)); 488 | chunk.pos = 0; 489 | } else if (chunk.pos >= 7 && chunk.large && chunk.run == 0) { 490 | /* Status Vector Chunk containing 7 2-bits symbols */ 491 | uint16_t status = build_rtcpfb_chunk_status( 492 | chunk.ack, chunk.pos, true); 493 | CHECK(rtp_write_u16(buf, pos, status)); 494 | uint8_t left = chunk.pos - 7; 495 | for (j = 0; j < left; ++j) 496 | chunk.ack[j] = chunk.ack[j + 7]; 497 | chunk.pos = left; 498 | } else if (chunk.pos >= STATUS_VECTOR_CHUNK_ACK_LG && 499 | chunk.ack[0] != 500 | rtpfb->feedbacks[i].pkt_status_symbol) { 501 | uint16_t status = 502 | (chunk.ack[0] << RUN_LENGTH_CHUNK_ACK_LG) | 503 | chunk.pos; 504 | CHECK(rtp_write_u16(buf, pos, status)); 505 | chunk.pos = 0; 506 | } 507 | if (chunk.pos == 0) { 508 | chunk.large = 0; 509 | chunk.run = 1; 510 | } 511 | 512 | if (chunk.pos < STATUS_VECTOR_CHUNK_ACK_LG) { 513 | chunk.ack[chunk.pos] = 514 | rtpfb->feedbacks[i].pkt_status_symbol; 515 | if (rtpfb->feedbacks[i].pkt_status_symbol > 1) 516 | chunk.large = 1; 517 | if (chunk.pos > 0 && 518 | chunk.ack[chunk.pos - 1] != chunk.ack[chunk.pos]) 519 | chunk.run = 0; 520 | } 521 | chunk.pos++; 522 | } 523 | 524 | if (chunk.pos != 0) { 525 | if (chunk.run) { 526 | uint16_t status = 527 | (chunk.ack[0] << RUN_LENGTH_CHUNK_ACK_LG) | 528 | chunk.pos; 529 | CHECK(rtp_write_u16(buf, pos, status)); 530 | } else if (chunk.large) { 531 | /* Status Vector Chunk containing 7 2-bits symbols with 532 | * long delta */ 533 | 534 | /* Write a first chunk if needed */ 535 | if (chunk.pos >= 7) { 536 | uint16_t status = build_rtcpfb_chunk_status( 537 | chunk.ack, chunk.pos, true); 538 | CHECK(rtp_write_u16(buf, pos, status)); 539 | uint8_t left = chunk.pos - 7; 540 | for (j = 0; j < left; ++j) 541 | chunk.ack[j] = chunk.ack[j + 7]; 542 | chunk.pos = left; 543 | } 544 | if (chunk.pos != 0) { 545 | uint16_t status = build_rtcpfb_chunk_status( 546 | chunk.ack, chunk.pos, true); 547 | CHECK(rtp_write_u16(buf, pos, status)); 548 | } 549 | } else { 550 | /* Status Vector Chunk containing 14 1-bits symbols 551 | * with short delta */ 552 | uint16_t status = build_rtcpfb_chunk_status( 553 | chunk.ack, chunk.pos, false); 554 | CHECK(rtp_write_u16(buf, pos, status)); 555 | } 556 | } 557 | 558 | for (i = 0; i < rtpfb->status_count; ++i) { 559 | int16_t delta = rtpfb->feedbacks[i].recv_delta; 560 | if (rtpfb->feedbacks[i].pkt_status_symbol == 1) 561 | CHECK(rtp_write_u8(buf, pos, delta)); 562 | else if (rtpfb->feedbacks[i].pkt_status_symbol == 2) 563 | CHECK(rtp_write_u16(buf, pos, delta)); 564 | } 565 | 566 | while ((*pos) % 4) 567 | CHECK(rtp_write_u8(buf, pos, 0)); 568 | 569 | /* Write header */ 570 | memset(&header, 0, sizeof(header)); 571 | header.flags = 572 | (RTCP_PKT_VERSION << RTCP_PKT_HEADER_FLAGS_VERSION_SHIFT) | 573 | (15 << RTCP_PKT_HEADER_FLAGS_COUNT_SHIFT); 574 | header.type = RTCP_PKT_TYPE_RTPFB; 575 | header.len = ((*pos - header_pos) / 4) - 1; 576 | CHECK(rtcp_pkt_write_header(buf, &header_pos, &header)); 577 | 578 | out: 579 | return res; 580 | } 581 | 582 | 583 | static int rtcp_pkt_read_header(const struct pomp_buffer *buf, 584 | size_t *pos, 585 | struct rtcp_pkt_header *header) 586 | { 587 | int res = 0; 588 | CHECK(rtp_read_u8(buf, pos, &header->flags)); 589 | CHECK(rtp_read_u8(buf, pos, &header->type)); 590 | CHECK(rtp_read_u16(buf, pos, &header->len)); 591 | out: 592 | return res; 593 | } 594 | 595 | 596 | /** 597 | * 6.4 Sender and Receiver Reports 598 | * 6.4.1 SR: Sender Report RTCP Packet 599 | * 6.4.2 RR: Receiver Report RTCP Packet 600 | */ 601 | static int rtcp_pkt_read_report_block(const struct pomp_buffer *buf, 602 | size_t *pos, 603 | const struct rtcp_pkt_header *header, 604 | struct rtcp_pkt_report_block *rb) 605 | { 606 | int res = 0; 607 | uint32_t fraction_lost = 0; 608 | CHECK(rtp_read_u32(buf, pos, &rb->ssrc)); 609 | CHECK(rtp_read_u32(buf, pos, &fraction_lost)); 610 | rb->fraction = (fraction_lost >> 24) & 0xff; 611 | rb->lost = fraction_lost & 0xffffff; 612 | CHECK(rtp_read_u32(buf, pos, &rb->ext_highest_seqnum)); 613 | CHECK(rtp_read_u32(buf, pos, &rb->jitter)); 614 | CHECK(rtp_read_u16(buf, pos, &rb->lsr.seconds)); 615 | CHECK(rtp_read_u16(buf, pos, &rb->lsr.fraction)); 616 | CHECK(rtp_read_u32(buf, pos, &rb->dlsr)); 617 | out: 618 | return res; 619 | } 620 | 621 | 622 | /** 623 | * 6.4.1 SR: Sender Report RTCP Packet 624 | */ 625 | static int rtcp_pkt_read_sender_report(const struct pomp_buffer *buf, 626 | size_t *pos, 627 | size_t end, 628 | const struct rtcp_pkt_header *header, 629 | const struct rtcp_pkt_read_cbs *cbs, 630 | void *userdata) 631 | { 632 | int res = 0; 633 | struct rtcp_pkt_sender_report sr; 634 | memset(&sr, 0, sizeof(sr)); 635 | 636 | CHECK(rtp_read_u32(buf, pos, &sr.ssrc)); 637 | CHECK(rtp_read_u32(buf, pos, &sr.ntp_timestamp.seconds)); 638 | CHECK(rtp_read_u32(buf, pos, &sr.ntp_timestamp.fraction)); 639 | CHECK(rtp_read_u32(buf, pos, &sr.rtp_timestamp)); 640 | CHECK(rtp_read_u32(buf, pos, &sr.sender_packet_count)); 641 | CHECK(rtp_read_u32(buf, pos, &sr.sender_byte_count)); 642 | 643 | sr.report_count = RTCP_PKT_HEADER_FLAGS(COUNT, header->flags); 644 | for (uint32_t i = 0; i < sr.report_count; i++) { 645 | CHECK(rtcp_pkt_read_report_block( 646 | buf, pos, header, &sr.reports[i])); 647 | } 648 | 649 | if (cbs->sender_report != NULL) 650 | (*cbs->sender_report)(&sr, userdata); 651 | 652 | out: 653 | return res; 654 | } 655 | 656 | 657 | /** 658 | * 6.4.2 RR: Receiver Report RTCP Packet 659 | */ 660 | static int rtcp_pkt_read_receiver_report(const struct pomp_buffer *buf, 661 | size_t *pos, 662 | size_t end, 663 | const struct rtcp_pkt_header *header, 664 | const struct rtcp_pkt_read_cbs *cbs, 665 | void *userdata) 666 | { 667 | int res = 0; 668 | struct rtcp_pkt_receiver_report rr; 669 | memset(&rr, 0, sizeof(rr)); 670 | 671 | CHECK(rtp_read_u32(buf, pos, &rr.ssrc)); 672 | 673 | rr.report_count = RTCP_PKT_HEADER_FLAGS(COUNT, header->flags); 674 | for (uint32_t i = 0; i < rr.report_count; i++) { 675 | CHECK(rtcp_pkt_read_report_block( 676 | buf, pos, header, &rr.reports[i])); 677 | } 678 | 679 | if (cbs->receiver_report != NULL) 680 | (*cbs->receiver_report)(&rr, userdata); 681 | 682 | out: 683 | return res; 684 | } 685 | 686 | 687 | /** 688 | * 6.5 SDES: Source Description RTCP Packet 689 | * 6.5.8 PRIV: Private Extensions SDES Item 690 | */ 691 | static int rtcp_pkt_read_sdes_item(const struct pomp_buffer *buf, 692 | size_t *pos, 693 | size_t end, 694 | const struct rtcp_pkt_header *header, 695 | uint32_t ssrc, 696 | const struct rtcp_pkt_read_cbs *cbs, 697 | void *userdata) 698 | { 699 | int res = 0; 700 | struct rtcp_pkt_sdes_item item; 701 | memset(&item, 0, sizeof(item)); 702 | 703 | CHECK(rtp_read_u8(buf, pos, &item.type)); 704 | 705 | CHECK(rtp_read_u8(buf, pos, &item.data_len)); 706 | if (item.data_len > end - *pos) { 707 | res = -EIO; 708 | ULOGE("sdes: bad length: %zu (%u)", end - *pos, item.data_len); 709 | goto out; 710 | } 711 | 712 | if (item.data_len != 0) { 713 | CHECK(pomp_buffer_cread( 714 | buf, pos, (const void **)&item.data, item.data_len)); 715 | if (item.type == RTCP_PKT_SDES_TYPE_PRIV) { 716 | /* Private item data: prefix_len + prefix + value */ 717 | item.priv.prefix_len = *item.data; 718 | if (item.priv.prefix_len + 1 > item.data_len) { 719 | res = -EIO; 720 | ULOGE("sdes: bad prefix length: %u (%u)", 721 | item.priv.prefix_len, 722 | item.data_len); 723 | goto out; 724 | } 725 | 726 | /* Setup private item fields */ 727 | item.priv.prefix = item.data + 1; 728 | item.priv.value_len = 729 | item.data_len - item.priv.prefix_len - 1; 730 | item.priv.value = item.data + item.priv.prefix_len + 1; 731 | } 732 | } 733 | 734 | if (cbs->sdes_item != NULL) 735 | (*cbs->sdes_item)(ssrc, &item, userdata); 736 | 737 | out: 738 | return res; 739 | } 740 | 741 | 742 | /** 743 | * 6.5 SDES: Source Description RTCP Packet 744 | */ 745 | static int rtcp_pkt_read_sdes_chunk(const struct pomp_buffer *buf, 746 | size_t *pos, 747 | size_t end, 748 | const struct rtcp_pkt_header *header, 749 | const struct rtcp_pkt_read_cbs *cbs, 750 | void *userdata) 751 | { 752 | int res = 0; 753 | uint8_t type = 0; 754 | uint32_t ssrc = 0; 755 | 756 | CHECK(rtp_read_u32(buf, pos, &ssrc)); 757 | 758 | while (*pos < end) { 759 | /* Read item type */ 760 | CHECK(rtp_read_u8(buf, pos, &type)); 761 | if (type == RTCP_PKT_SDES_TYPE_END) 762 | break; 763 | 764 | /* Rewind and read item */ 765 | *pos -= 1; 766 | CHECK(rtcp_pkt_read_sdes_item( 767 | buf, pos, end, header, ssrc, cbs, userdata)); 768 | } 769 | 770 | /* Align */ 771 | while (*pos < end && *pos % 4 != 0) 772 | *pos += 1; 773 | 774 | out: 775 | return res; 776 | } 777 | 778 | 779 | /** 780 | * 6.5 SDES: Source Description RTCP Packet 781 | */ 782 | static int rtcp_pkt_read_sdes(const struct pomp_buffer *buf, 783 | size_t *pos, 784 | size_t end, 785 | const struct rtcp_pkt_header *header, 786 | const struct rtcp_pkt_read_cbs *cbs, 787 | void *userdata) 788 | { 789 | int res = 0; 790 | uint32_t chunk_count = 0; 791 | 792 | chunk_count = RTCP_PKT_HEADER_FLAGS(COUNT, header->flags); 793 | for (uint32_t i = 0; i < chunk_count; i++) 794 | CHECK(rtcp_pkt_read_sdes_chunk( 795 | buf, pos, end, header, cbs, userdata)); 796 | 797 | out: 798 | return res; 799 | } 800 | 801 | 802 | /** 803 | * 6.6 BYE: Goodbye RTCP Packet 804 | */ 805 | static int rtcp_pkt_read_bye(const struct pomp_buffer *buf, 806 | size_t *pos, 807 | size_t end, 808 | const struct rtcp_pkt_header *header, 809 | const struct rtcp_pkt_read_cbs *cbs, 810 | void *userdata) 811 | { 812 | int res = 0; 813 | struct rtcp_pkt_bye bye; 814 | memset(&bye, 0, sizeof(bye)); 815 | 816 | /* Read sources */ 817 | bye.source_count = RTCP_PKT_HEADER_FLAGS(COUNT, header->flags); 818 | for (uint32_t i = 0; i < bye.source_count; i++) 819 | CHECK(rtp_read_u32(buf, pos, &bye.sources[i])); 820 | 821 | /* Read optional reason */ 822 | if (*pos < end) { 823 | CHECK(rtp_read_u8(buf, pos, &bye.reason_len)); 824 | if (end - *pos < bye.reason_len) { 825 | res = -EIO; 826 | ULOGW("bye: bad length: %zu (%u)", 827 | end - *pos, 828 | bye.reason_len); 829 | goto out; 830 | } 831 | CHECK(pomp_buffer_cread( 832 | buf, pos, (const void **)&bye.reason, bye.reason_len)); 833 | } 834 | 835 | if (cbs->bye != NULL) 836 | (*cbs->bye)(&bye, userdata); 837 | 838 | out: 839 | return res; 840 | } 841 | 842 | 843 | /** 844 | * 6.7 APP: Application-Defined RTCP Packet 845 | */ 846 | static int rtcp_pkt_read_app(const struct pomp_buffer *buf, 847 | size_t *pos, 848 | size_t end, 849 | const struct rtcp_pkt_header *header, 850 | const struct rtcp_pkt_read_cbs *cbs, 851 | void *userdata) 852 | { 853 | int res = 0; 854 | struct rtcp_pkt_app app; 855 | memset(&app, 0, sizeof(app)); 856 | 857 | /* Read data */ 858 | app.subtype = RTCP_PKT_HEADER_FLAGS(COUNT, header->flags); 859 | CHECK(rtp_read_u32(buf, pos, &app.ssrc)); 860 | CHECK(rtp_read_u32(buf, pos, &app.name)); 861 | if (*pos < end) { 862 | app.data_len = end - *pos; 863 | CHECK(pomp_buffer_cread( 864 | buf, pos, (const void **)&app.data, app.data_len)); 865 | } 866 | 867 | if (cbs->app != NULL) 868 | (*cbs->app)(&app, userdata); 869 | 870 | out: 871 | return res; 872 | } 873 | 874 | 875 | static int 876 | rtcp_pkt_read_rtpfb_run_length_chunk(uint16_t chunk, 877 | const struct rtcp_pkt_rtpfb_report *report, 878 | size_t *pos) 879 | { 880 | int res = 0; 881 | size_t j = *pos; 882 | size_t len = (chunk & 0x1FFF) + j; 883 | uint8_t status_symbol; 884 | 885 | CHECK(report->status_count - len); 886 | status_symbol = (chunk >> RUN_LENGTH_CHUNK_ACK_LG) & 0x03; 887 | for (; j < len; j++) { 888 | report->feedbacks[j].seq_num = report->base_seq + j; 889 | report->feedbacks[j].pkt_status_symbol = status_symbol; 890 | } 891 | *pos = j; 892 | 893 | out: 894 | return res; 895 | } 896 | 897 | 898 | static int rtcp_pkt_read_rtpfb_status_vector_chunk( 899 | uint16_t chunk, 900 | const struct rtcp_pkt_rtpfb_report *report, 901 | size_t *pos) 902 | { 903 | int res = 0; 904 | size_t len = 0; 905 | size_t i = *pos; 906 | size_t j = 1; 907 | 908 | if ((chunk & 0x4000) == 0) { 909 | /* Small delta chunk */ 910 | len = STATUS_VECTOR_CHUNK_ACK_LG; 911 | if (len + i >= report->status_count) 912 | len = report->status_count - i; 913 | CHECK(report->status_count - len); 914 | while (j <= len) { 915 | const uint8_t offset = STATUS_VECTOR_CHUNK_ACK_LG - j; 916 | const uint8_t symbol = (chunk >> offset) & 0x01; 917 | report->feedbacks[i].pkt_status_symbol = symbol; 918 | report->feedbacks[i].seq_num = report->base_seq + i; 919 | ++i; 920 | ++j; 921 | } 922 | } else { 923 | /* Large delta chunk */ 924 | len = STATUS_VECTOR_CHUNK_ACK_LG / 2; 925 | if (len + i >= report->status_count) 926 | len = report->status_count - i; 927 | CHECK(report->status_count - len); 928 | while (j <= len) { 929 | const uint8_t offset = 930 | STATUS_VECTOR_CHUNK_ACK_LG - j * 2; 931 | const uint8_t symbol = (chunk >> offset) & 0x03; 932 | report->feedbacks[i].pkt_status_symbol = symbol; 933 | report->feedbacks[i].seq_num = report->base_seq + i; 934 | ++i; 935 | ++j; 936 | } 937 | } 938 | *pos = i; 939 | 940 | out: 941 | return res; 942 | } 943 | 944 | 945 | static int rtcp_pkt_read_rtpfb(const struct pomp_buffer *buf, 946 | size_t *pos, 947 | size_t end, 948 | const struct rtcp_pkt_header *header, 949 | const struct rtcp_pkt_read_cbs *cbs, 950 | void *userdata) 951 | { 952 | int res = 0; 953 | size_t i = 0; 954 | struct rtcp_pkt_rtpfb_report report; 955 | report.status_count = 0; 956 | report.feedbacks = NULL; 957 | CHECK(rtp_read_u32(buf, pos, &report.sender_ssrc)); 958 | CHECK(rtp_read_u32(buf, pos, &report.media_ssrc)); 959 | CHECK(rtp_read_u16(buf, pos, &report.base_seq)); 960 | CHECK(rtp_read_u16(buf, pos, &report.status_count)); 961 | CHECK(rtp_read_u32(buf, pos, &report.ref_time)); 962 | report.feedback_pkt_count = report.ref_time & 0xFF; 963 | report.ref_time = report.ref_time >> 8; 964 | 965 | if (report.status_count > RTPFB_MAX_PKT) { 966 | res = -E2BIG; 967 | goto out; 968 | } 969 | report.feedbacks = calloc(report.status_count, 970 | sizeof(struct rtcp_pkt_rtpfb_feedback)); 971 | if (report.feedbacks == NULL) { 972 | res = -ENOMEM; 973 | goto out; 974 | } 975 | 976 | while (i < report.status_count) { 977 | uint16_t chunk; 978 | CHECK(rtp_read_u16(buf, pos, &chunk)); 979 | if ((chunk & 0x8000) == 0) { 980 | rtcp_pkt_read_rtpfb_run_length_chunk( 981 | chunk, &report, &i); 982 | } else { 983 | rtcp_pkt_read_rtpfb_status_vector_chunk( 984 | chunk, &report, &i); 985 | } 986 | } 987 | 988 | uint8_t short_delta; 989 | uint16_t long_delta; 990 | for (i = 0; i < report.status_count; ++i) { 991 | switch (report.feedbacks[i].pkt_status_symbol) { 992 | case 0: 993 | case 3: 994 | report.feedbacks[i].recv_delta = 0; 995 | break; 996 | case 1: 997 | CHECK(rtp_read_u8(buf, pos, &short_delta)); 998 | report.feedbacks[i].recv_delta = short_delta; 999 | break; 1000 | case 2: 1001 | CHECK(rtp_read_u16(buf, pos, &long_delta)); 1002 | report.feedbacks[i].recv_delta = long_delta; 1003 | break; 1004 | } 1005 | } 1006 | 1007 | if (cbs->rtpfb_report != NULL) 1008 | (*cbs->rtpfb_report)(&report, userdata); 1009 | 1010 | out: 1011 | if (report.feedbacks != NULL) 1012 | free(report.feedbacks); 1013 | 1014 | return res; 1015 | } 1016 | 1017 | 1018 | int rtcp_pkt_read(const struct pomp_buffer *buf, 1019 | const struct rtcp_pkt_read_cbs *cbs, 1020 | void *userdata) 1021 | { 1022 | int res = 0; 1023 | struct rtcp_pkt_header header; 1024 | uint8_t version = 0; 1025 | size_t len = 0, end = 0, pos = 0; 1026 | 1027 | ULOG_ERRNO_RETURN_ERR_IF(buf == NULL, EINVAL); 1028 | ULOG_ERRNO_RETURN_ERR_IF(cbs == NULL, EINVAL); 1029 | 1030 | pomp_buffer_get_cdata(buf, NULL, &len, NULL); 1031 | while (pos < len) { 1032 | /* Read header */ 1033 | if (len - pos < RTCP_PKT_HEADER_SIZE) { 1034 | res = -EIO; 1035 | ULOGE("hdr: bad length: %zu (%u)", 1036 | len - pos, 1037 | RTCP_PKT_HEADER_SIZE); 1038 | goto out; 1039 | } 1040 | memset(&header, 0, sizeof(header)); 1041 | CHECK(rtcp_pkt_read_header(buf, &pos, &header)); 1042 | 1043 | /* Check version */ 1044 | version = RTCP_PKT_HEADER_FLAGS(VERSION, header.flags); 1045 | if (version != RTCP_PKT_VERSION) { 1046 | res = -EIO; 1047 | ULOGE("hdr: bad version: %u (%u)", 1048 | version, 1049 | RTCP_PKT_VERSION); 1050 | goto out; 1051 | } 1052 | 1053 | /* Check length; the length in the header is the number 1054 | * of 32-bit words in the packet, not including the 1055 | * header itself */ 1056 | if (len - pos < header.len * 4) { 1057 | res = -EIO; 1058 | ULOGE("hdr: bad length: %zu (%u)", 1059 | len - pos, 1060 | header.len * 4); 1061 | goto out; 1062 | } 1063 | 1064 | /* Ignore errors during payload to try to read other packets */ 1065 | end = pos + header.len * 4; 1066 | switch (header.type) { 1067 | case RTCP_PKT_TYPE_SR: 1068 | rtcp_pkt_read_sender_report( 1069 | buf, &pos, end, &header, cbs, userdata); 1070 | break; 1071 | case RTCP_PKT_TYPE_RR: 1072 | rtcp_pkt_read_receiver_report( 1073 | buf, &pos, end, &header, cbs, userdata); 1074 | break; 1075 | case RTCP_PKT_TYPE_SDES: 1076 | rtcp_pkt_read_sdes( 1077 | buf, &pos, end, &header, cbs, userdata); 1078 | break; 1079 | case RTCP_PKT_TYPE_BYE: 1080 | rtcp_pkt_read_bye( 1081 | buf, &pos, end, &header, cbs, userdata); 1082 | break; 1083 | case RTCP_PKT_TYPE_APP: 1084 | rtcp_pkt_read_app( 1085 | buf, &pos, end, &header, cbs, userdata); 1086 | break; 1087 | case RTCP_PKT_TYPE_RTPFB: 1088 | rtcp_pkt_read_rtpfb( 1089 | buf, &pos, end, &header, cbs, userdata); 1090 | } 1091 | 1092 | /* In any case, continue after the payload based on the length 1093 | * given in the header */ 1094 | pos = end; 1095 | } 1096 | 1097 | out: 1098 | return res; 1099 | } 1100 | --------------------------------------------------------------------------------