diff options
Diffstat (limited to 'tinyRTP')
50 files changed, 9887 insertions, 0 deletions
diff --git a/tinyRTP/Makefile.am b/tinyRTP/Makefile.am new file mode 100644 index 0000000..02fc587 --- /dev/null +++ b/tinyRTP/Makefile.am @@ -0,0 +1,49 @@ +lib_LTLIBRARIES = libtinyRTP.la +libtinyRTP_la_LIBADD = ../tinySAK/libtinySAK.la ../tinyNET/libtinyNET.la ../tinyMEDIA/libtinyMEDIA.la +libtinyRTP_la_CPPFLAGS = -I../tinySAK/src -I../tinyNET/src -I../tinyMEDIA/include -Iinclude + +if USE_SRTP +libtinyRTP_la_LIBADD += ${LIBSRTP_LIBADD} +libtinyRTP_la_CPPFLAGS += -DHAVE_SRTP=1 +endif + +libtinyRTP_la_SOURCES = \ + src/trtp.c \ + src/trtp_manager.c \ + src/trtp_srtp.c + +libtinyRTP_la_SOURCES += src/rtcp/trtp_rtcp_header.c \ + src/rtcp/trtp_rtcp_packet.c \ + src/rtcp/trtp_rtcp_rblock.c \ + src/rtcp/trtp_rtcp_report.c \ + src/rtcp/trtp_rtcp_report_bye.c \ + src/rtcp/trtp_rtcp_report_fb.c \ + src/rtcp/trtp_rtcp_report_rr.c \ + src/rtcp/trtp_rtcp_report_sdes.c \ + src/rtcp/trtp_rtcp_report_sr.c \ + src/rtcp/trtp_rtcp_report_xr.c \ + src/rtcp/trtp_rtcp_sdes_chunck.c \ + src/rtcp/trtp_rtcp_sdes_item.c \ + src/rtcp/trtp_rtcp_session.c + +libtinyRTP_la_SOURCES += src/rtp/trtp_rtp_header.c \ + src/rtp/trtp_rtp_packet.c \ + src/rtp/trtp_rtp_session.c + + +libtinyRTP_la_LDFLAGS = $LDFLAGS -no-undefined +if TARGET_OS_IS_ANDROID +libtinyRTP_la_LDFLAGS += -static +endif + +_includedir = $(includedir)/tinyrtp +_include_HEADERS = include/*.h +__includedir = $(includedir)/tinyrtp/tinyrtp +__include_HEADERS = include/tinyrtp/*.h +rtcp_includedir = $(includedir)/tinyrtp/tinyrtp/rtcp +rtcp_include_HEADERS = include/tinyrtp/rtcp/*.h +rtp_includedir = $(includedir)/tinyrtp/tinyrtp/rtp +rtp_include_HEADERS = include/tinyrtp/rtp/*.h + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = tinyRTP.pc
\ No newline at end of file diff --git a/tinyRTP/droid-makefile b/tinyRTP/droid-makefile new file mode 100644 index 0000000..a56b89f --- /dev/null +++ b/tinyRTP/droid-makefile @@ -0,0 +1,59 @@ +APP := lib$(PROJECT)_$(MARCH).$(EXT) + +# SRTP (Default: enabled) +ifeq ($(SRTP),no) + LIBSRTP_CFLAGS := -DHAVE_SRTP=0 +else + LIBSRTP_CFLAGS := -DHAVE_SRTP=1 + LIBSRTP_LDFLAGS := -lsrtp +endif + +THIRDPARTIES_INC := ../thirdparties/android/include +CFLAGS := $(CFLAGS_LIB) -I$(THIRDPARTIES_INC) $(LIBSRTP_CFLAGS) -I../tinySAK/src -I../tinyNET/src -I./include -I../tinyMEDIA/include +LDFLAGS := $(LDFLAGS_LIB) $(LIBSRTP_LDFLAGS) -ltinySAK_$(MARCH) -ltinyNET_$(MARCH) -ltinyMEDIA_$(MARCH) + +all: $(APP) + +OBJS = \ + src/trtp.o \ + src/trtp_manager.o \ + src/trtp_srtp.o + +## RTCP +OBJS += src/rtcp/trtp_rtcp_header.o \ + src/rtcp/trtp_rtcp_packet.o \ + src/rtcp/trtp_rtcp_rblock.o \ + src/rtcp/trtp_rtcp_report.o \ + src/rtcp/trtp_rtcp_report_bye.o \ + src/rtcp/trtp_rtcp_report_fb.o \ + src/rtcp/trtp_rtcp_report_rr.o \ + src/rtcp/trtp_rtcp_report_sdes.o \ + src/rtcp/trtp_rtcp_report_sr.o \ + src/rtcp/trtp_rtcp_report_xr.o \ + src/rtcp/trtp_rtcp_sdes_chunck.o \ + src/rtcp/trtp_rtcp_sdes_item.o \ + src/rtcp/trtp_rtcp_session.o + +## RTP +OBJS += src/rtp/trtp_rtp_header.o \ + src/rtp/trtp_rtp_packet.o \ + src/rtp/trtp_rtp_session.o + + +$(APP): $(OBJS) +ifeq ($(EXT), a) + $(AR) rcs $@ $^ +else + $(CC) $(LDFLAGS) -o $@ $^ +endif + +%.o: %.c + $(CC) -c $(INCLUDE) $(CFLAGS) $< -o $@ + +install: $(APP) + $(ANDROID_SDK_ROOT)/tools/adb remount + $(ANDROID_SDK_ROOT)/tools/adb push $(APP) $(LIB_DIR)/$(APP) + $(ANDROID_SDK_ROOT)/tools/adb shell chmod 777 $(LIB_DIR)/$(APP) + +clean: + @rm -f $(OBJS) $(APP)
\ No newline at end of file diff --git a/tinyRTP/include/tinyrtp.h b/tinyRTP/include/tinyrtp.h new file mode 100644 index 0000000..8707f81 --- /dev/null +++ b/tinyRTP/include/tinyrtp.h @@ -0,0 +1,43 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +/**@file tinyrtp.h + * @brief tinyRTP (Real-time Transport Protocol) API. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + + */ +#ifndef TINYRTP_TINYRTP_H +#define TINYRTP_TINYRTP_H + +/* === tinyNET (tinyNET/src) === */ +#include "tnet.h" + +/* === tinySAK (tinySAK/src)=== */ +#include "tsk.h" + +#include "tinyrtp/rtp/trtp_rtp_header.h" +#include "tinyrtp/rtp/trtp_rtp_packet.h" +#include "tinyrtp/trtp_manager.h" + +#endif /* TINYRTP_TINYRTP_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_header.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_header.h new file mode 100644 index 0000000..9cf6a1a --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_header.h @@ -0,0 +1,62 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_HEADER_H +#define TINYRTP_RTCP_HEADER_H + +#include "tinyrtp_config.h" +#include "tinyrtp/rtcp/trtp_rtcp_packet.h" + +#include "tsk_buffer.h" +#include "tsk_list.h" + +#define TRTP_RTCP_HEADER_SIZE 4 +#define TRTP_RTCP_HEADER_VERSION_DEFAULT 2 + +TRTP_BEGIN_DECLS + +typedef struct trtp_rtcp_header_s +{ + TSK_DECLARE_OBJECT; + + unsigned version:2; + unsigned padding:1; + unsigned rc:5; + trtp_rtcp_packet_type_t type; /**< Packet Type on 8bits */ + uint16_t length_in_words_minus1; /**< The length of this RTCP packet in 32-bit words minus one */ + uint32_t length_in_bytes; +} +trtp_rtcp_header_t; + +#define TRTP_DECLARE_RTCP_HEADER trtp_rtcp_header_t __header__ +typedef tsk_list_t trtp_rtcp_headers_L_t; /**< List of @ref trtp_rtcp_header_t elements */ + +TINYRTP_API trtp_rtcp_header_t* trtp_rtcp_header_create_null(); +TINYRTP_API trtp_rtcp_header_t* trtp_rtcp_header_create(uint8_t version, uint8_t padding, uint8_t rc, trtp_rtcp_packet_type_t type, uint16_t length_in_bytes); +TINYRTP_API int trtp_rtcp_header_serialize_to(const trtp_rtcp_header_t *self, void* data, tsk_size_t size); +TINYRTP_API trtp_rtcp_header_t* trtp_rtcp_header_deserialize(const void *data, tsk_size_t size); +TINYRTP_API int trtp_rtcp_header_deserialize_to(trtp_rtcp_header_t** self, const void *data, tsk_size_t size); + +TINYRTP_GEXTERN const tsk_object_def_t *trtp_rtcp_header_def_t; + +TRTP_END_DECLS + +#endif /* TINYRTP_RTCP_HEADER_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_packet.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_packet.h new file mode 100644 index 0000000..9045be7 --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_packet.h @@ -0,0 +1,74 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_PACKET_H +#define TINYRTP_RTCP_PACKET_H + +#include "tinyrtp_config.h" + +#include "tsk_buffer.h" +#include "tsk_list.h" + +TRTP_BEGIN_DECLS + +#define TRTP_RTCP_PACKET(self) ((trtp_rtcp_packet_t*)(self)) +#define TRTP_DECLARE_RTCP_PACKET trtp_rtcp_packet_t __packet__ + +// RFC 3550 12.1 RTCP Packet Types +// RFC 4585 +// RFC 5104 (FIXME: not supported yet!) +typedef enum trtp_rtcp_packet_type_e +{ + trtp_rtcp_packet_type_sr = 200, + trtp_rtcp_packet_type_rr = 201, + trtp_rtcp_packet_type_sdes = 202, + trtp_rtcp_packet_type_bye = 203, + trtp_rtcp_packet_type_app = 204, + trtp_rtcp_packet_type_rtpfb = 205, + trtp_rtcp_packet_type_psfb = 206 +} +trtp_rtcp_packet_type_t; + +typedef struct trtp_rtcp_packet_s +{ + TSK_DECLARE_OBJECT; + + struct trtp_rtcp_header_s *header; +} +trtp_rtcp_packet_t; + +typedef tsk_list_t trtp_rtcp_packets_L_t; /**< List of @ref trtp_rtcp_packet_t elements */ + +trtp_rtcp_packet_t* trtp_rtcp_packet_create(struct trtp_rtcp_header_s* header); +int trtp_rtcp_packet_init(trtp_rtcp_packet_t* self, uint8_t version, uint8_t padding, uint8_t rc, trtp_rtcp_packet_type_t type, uint16_t length_in_bytes); +trtp_rtcp_packet_t* trtp_rtcp_packet_deserialize(const void* data, tsk_size_t size); +int trtp_rtcp_packet_serialize_to(const trtp_rtcp_packet_t* self, void* data, tsk_size_t size); +tsk_buffer_t* trtp_rtcp_packet_serialize(const trtp_rtcp_packet_t* self, tsk_size_t num_bytes_pad); +int trtp_rtcp_packet_add_packet(trtp_rtcp_packet_t* self, trtp_rtcp_packet_t* packet, tsk_bool_t front); +TINYRTP_API const trtp_rtcp_packet_t* trtp_rtcp_packet_get_at(const trtp_rtcp_packet_t* self, trtp_rtcp_packet_type_t type, tsk_size_t index); +TINYRTP_API const trtp_rtcp_packet_t* trtp_rtcp_packet_get(const trtp_rtcp_packet_t* self, trtp_rtcp_packet_type_t type); +tsk_size_t trtp_rtcp_packet_get_size(const trtp_rtcp_packet_t* self); + +int trtp_rtcp_packet_deinit(trtp_rtcp_packet_t* self); + +TRTP_END_DECLS + +#endif /* TINYRTP_RTCP_PACKET_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_rblock.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_rblock.h new file mode 100644 index 0000000..5ea54e6 --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_rblock.h @@ -0,0 +1,59 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_REPORT_RB_H +#define TINYRTP_RTCP_REPORT_RB_H + +#include "tinyrtp_config.h" + +#include "tsk_list.h" + +TRTP_BEGIN_DECLS + +#define TRTP_RTCP_RBLOCK_SIZE 24 + +#define TRTP_RTCP_RBLOCK(self) ((trtp_rtcp_rblock_t*)(self)) + +// RFC 3550 6.4.1 SR: Sender Report RTCP Packet => Report block part +typedef struct trtp_rtcp_rblock_s +{ + TSK_DECLARE_OBJECT; + + uint32_t ssrc; /* data source being reported */ + unsigned int fraction:8; /* fraction lost since last SR/RR */ + int cumulative_no_lost:24; /* cumul. no. pkts lost (signed!) */ + uint32_t last_seq; /* extended last seq. no. received */ + uint32_t jitter; /* interarrival jitter */ + uint32_t lsr; /* last SR packet from this source */ + uint32_t dlsr; /* delay since last SR packet */ +} +trtp_rtcp_rblock_t; + +typedef tsk_list_t trtp_rtcp_rblocks_L_t; /**< List of @ref trtp_rtcp_rblock_t elements */ + +trtp_rtcp_rblock_t* trtp_rtcp_rblock_create_null(); +trtp_rtcp_rblock_t* trtp_rtcp_rblock_deserialize(const void* data, tsk_size_t size); +int trtp_rtcp_rblock_deserialize_list(const void* data, tsk_size_t size, trtp_rtcp_rblocks_L_t* dest_list); +int trtp_rtcp_rblock_serialize_to(const trtp_rtcp_rblock_t* self, void* data, tsk_size_t size); + +TRTP_END_DECLS + +#endif /* TINYRTP_RTCP_REPORT_RB_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report.h new file mode 100644 index 0000000..c30351f --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report.h @@ -0,0 +1,32 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_REPORT_H +#define TINYRTP_RTCP_REPORT_H + +#include "tinyrtp_config.h" + +TRTP_BEGIN_DECLS + + +TRTP_END_DECLS + +#endif /* TINYRTP_RTCP_REPORT_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_bye.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_bye.h new file mode 100644 index 0000000..796e9fc --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_bye.h @@ -0,0 +1,45 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_REPORT_BYE_H +#define TINYRTP_RTCP_REPORT_BYE_H + +#include "tinyrtp_config.h" + +#include "tinyrtp/rtcp/trtp_rtcp_packet.h" + +typedef struct trtp_rtcp_report_bye_s +{ + TRTP_DECLARE_RTCP_PACKET; + + uint32_t *ssrc_list; + trtp_rtcp_packets_L_t* packets; +} +trtp_rtcp_report_bye_t; + +trtp_rtcp_report_bye_t* trtp_rtcp_report_bye_create_null(); +trtp_rtcp_report_bye_t* trtp_rtcp_report_bye_create(struct trtp_rtcp_header_s* header); +trtp_rtcp_report_bye_t* trtp_rtcp_report_bye_create_2(uint32_t ssrc); +trtp_rtcp_report_bye_t* trtp_rtcp_report_bye_deserialize(const void* data, tsk_size_t size); +int trtp_rtcp_report_bye_serialize_to(const trtp_rtcp_report_bye_t* self, void* data, tsk_size_t size); +tsk_size_t trtp_rtcp_report_bye_get_size(const trtp_rtcp_report_bye_t* self); + +#endif /* TINYRTP_RTCP_REPORT_BYE_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_fb.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_fb.h new file mode 100644 index 0000000..1ad30b4 --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_fb.h @@ -0,0 +1,152 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_REPORT_FB_H +#define TINYRTP_RTCP_REPORT_FB_H + +#include "tinyrtp_config.h" + +#include "tinyrtp/rtcp/trtp_rtcp_packet.h" + +#define TRTP_RTCP_REPORT_FB(self) ((trtp_rtcp_report_fb_t*)(self)) +#define TRTP_DECLARE_RTCP_FB_PACKET trtp_rtcp_report_fb_t __packet_fb__ + +// RFC 4585 6.1. Common Packet Format for Feedback Messages +typedef struct trtp_rtcp_report_fb_s +{ + TRTP_DECLARE_RTCP_PACKET; + + uint32_t ssrc_sender; /* SSRC of packet sender */ + uint32_t ssrc_media; /* SSRC of media source */ +} +trtp_rtcp_report_fb_t; + +typedef enum trtp_rtcp_rtpfb_fci_type_e +{ + trtp_rtcp_rtpfb_fci_type_nack = 1, // RFC 4585 + trtp_rtcp_rtpfb_fci_type_tmmbn = 4, // RFC 5104 +} +trtp_rtcp_rtpfb_fci_type_t; + +// Transport layer FB message +typedef struct trtp_rtcp_report_rtpfb_s +{ + TRTP_DECLARE_RTCP_FB_PACKET; + + trtp_rtcp_rtpfb_fci_type_t fci_type; + union{ + struct{ + tsk_size_t count; // not part of the specification + uint16_t* pid; // 16 bits + uint16_t* blp; // 16 bits + }nack; + struct{ + tsk_size_t count; // not part of the specification + uint32_t* ssrc; // 32 bits + uint8_t* MxTBR_Exp; // 6 bits + uint32_t* MxTBR_Mantissa; // 17 bits + uint16_t* MeasuredOverhead; // 9 bits + }tmmbn; + }; +} +trtp_rtcp_report_rtpfb_t; + +trtp_rtcp_report_rtpfb_t* trtp_rtcp_report_rtpfb_create_null(); +trtp_rtcp_report_rtpfb_t* trtp_rtcp_report_rtpfb_create(struct trtp_rtcp_header_s* header); +trtp_rtcp_report_rtpfb_t* trtp_rtcp_report_rtpfb_create_2(trtp_rtcp_rtpfb_fci_type_t fci_type, uint32_t ssrc_sender, uint32_t ssrc_media_src); +trtp_rtcp_report_rtpfb_t* trtp_rtcp_report_rtpfb_create_nack(uint32_t ssrc_sender, uint32_t ssrc_media_src, const uint16_t* seq_nums, tsk_size_t count); +trtp_rtcp_report_rtpfb_t* trtp_rtcp_report_rtpfb_deserialize(const void* data, tsk_size_t size); +int trtp_rtcp_report_rtpfb_serialize_to(const trtp_rtcp_report_rtpfb_t* self, void* data, tsk_size_t size); +tsk_size_t trtp_rtcp_report_rtpfb_get_size(const trtp_rtcp_report_rtpfb_t* self); + + +typedef enum trtp_rtcp_psfb_fci_type_e +{ + trtp_rtcp_psfb_fci_type_pli = 1, /* rfc 4585: Picture Loss Indication (PLI) */ + trtp_rtcp_psfb_fci_type_sli = 2, /* rfc 4585: Slice Loss Indication (SLI) */ + trtp_rtcp_psfb_fci_type_rpsi = 3, /* rfc 4585: Reference Picture Selection Indication (RPSI) */ + trtp_rtcp_psfb_fci_type_fir = 4, /* rfc 5104: Full Intra Request (FIR) Command*/ + trtp_rtcp_psfb_fci_type_afb = 15, /* rfc 4585: Application layer FB (AFB) message */ +} +trtp_rtcp_psfb_fci_type_t; + +/* rfc 4585: Application layer FB (AFB) message */ +typedef enum trtp_rtcp_psfb_afb_type_e +{ + trtp_rtcp_psfb_afb_type_none, + trtp_rtcp_psfb_afb_type_remb // draft-alvestrand-rmcat-remb-02 +} +trtp_rtcp_psfb_afb_type_t; + +// Payload-specific FB message +typedef struct trtp_rtcp_report_psfb_s +{ + TRTP_DECLARE_RTCP_FB_PACKET; + + trtp_rtcp_psfb_fci_type_t fci_type; + union{ + // struct{ //rfc 4585: 6.3.1.2 + // } pli; + struct{ // rfc 4585: 6.3.2.2 + uint16_t* first; + uint16_t* number; + uint8_t* pic_id; + } sli; + struct{ // rfc 4585: 6.3.3.2 + unsigned pb:8; + unsigned pt:7; + uint8_t* bytes; + } rpsi; + struct{// rfc 5104: 4.3.1.1 + tsk_size_t count; + uint32_t* ssrc; // 32 bits + uint8_t* seq_num; // 8 bits + }fir; + struct{ // rfc 4585: 6.4 + trtp_rtcp_psfb_afb_type_t type; + union{ + struct{ // draft-alvestrand-rmcat-remb-02: 2.2 + // MxTBR = mantissa * 2^exp = (mantissa << exp) bps + uint8_t num_ssrc; + uint8_t exp; // 6bits + uint32_t mantissa; // 18bits + uint32_t* ssrc_feedbacks; // 'num_ssrc'nth SSRC entries + }remb; + struct{ + uint8_t* bytes; // store bytes to allow reconstruction + }none; // unknown type + }; + }afb; + }; +} +trtp_rtcp_report_psfb_t; + +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create_null(); +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create(struct trtp_rtcp_header_s* header); +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create_2(trtp_rtcp_psfb_fci_type_t fci_type, uint32_t ssrc_sender, uint32_t ssrc_media_src); +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create_pli(uint32_t ssrc_sender, uint32_t ssrc_media_src); +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create_fir(uint8_t seq_num, uint32_t ssrc_sender, uint32_t ssrc_media_src); +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create_afb_remb(uint32_t ssrc_sender, const uint32_t* ssrc_media_src_list, uint32_t ssrc_media_src_list_count, uint32_t bitrate/*in bps*/); +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_deserialize(const void* data, tsk_size_t size); +int trtp_rtcp_report_psfb_serialize_to(const trtp_rtcp_report_psfb_t* self, void* data, tsk_size_t size); +tsk_size_t trtp_rtcp_report_psfb_get_size(const trtp_rtcp_report_psfb_t* self); + +#endif /* TINYRTP_RTCP_REPORT_FB_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_rr.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_rr.h new file mode 100644 index 0000000..2f25779 --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_rr.h @@ -0,0 +1,54 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_REPORT_RR_H +#define TINYRTP_RTCP_REPORT_RR_H + +#include "tinyrtp_config.h" + +#include "tinyrtp/rtcp/trtp_rtcp_packet.h" +#include "tinyrtp/rtcp/trtp_rtcp_rblock.h" + +TRTP_BEGIN_DECLS + +#define TRTP_RTCP_REPORT_RR(self) ((trtp_rtcp_report_rr_t*)(self)) + +// RFC 3550 6.4.2 RR: Receiver Report RTCP Packet +typedef struct trtp_rtcp_report_rr_s +{ + TRTP_DECLARE_RTCP_PACKET; + + uint32_t ssrc; + trtp_rtcp_rblocks_L_t* blocks; + trtp_rtcp_packets_L_t* packets; +} +trtp_rtcp_report_rr_t; + +trtp_rtcp_report_rr_t* trtp_rtcp_report_rr_create_null(); +trtp_rtcp_report_rr_t* trtp_rtcp_report_rr_create(struct trtp_rtcp_header_s* header); +trtp_rtcp_report_rr_t* trtp_rtcp_report_rr_create_2(uint32_t ssrc); +trtp_rtcp_report_rr_t* trtp_rtcp_report_rr_deserialize(const void* data, tsk_size_t size); +int trtp_rtcp_report_rr_serialize_to(const trtp_rtcp_report_rr_t* self, void* data, tsk_size_t size); +tsk_size_t trtp_rtcp_report_rr_get_size(const trtp_rtcp_report_rr_t* self); + +TRTP_END_DECLS + +#endif /* TINYRTP_RTCP_REPORT_RR_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_sdes.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_sdes.h new file mode 100644 index 0000000..04efb5c --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_sdes.h @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_REPORT_SDES_H +#define TINYRTP_RTCP_REPORT_SDES_H + +#include "tinyrtp_config.h" + +#include "tinyrtp/rtcp/trtp_rtcp_packet.h" +#include "tinyrtp/rtcp/trtp_rtcp_sdes_chunck.h" + +TRTP_BEGIN_DECLS + +#define TRTP_RTCP_REPORT_SDES(self) ((trtp_rtcp_report_sdes_t*)(self)) + +/* RFC 3550 6.5 SDES: Source Description RTCP Packet */ +typedef struct trtp_rtcp_report_sdes_s +{ + TRTP_DECLARE_RTCP_PACKET; + trtp_rtcp_sdes_chuncks_L_t* chuncks; +} +trtp_rtcp_report_sdes_t; + +typedef tsk_list_t trtp_rtcp_report_sdess_L_t; /**< List of @ref trtp_rtcp_report_sdes_t elements */ + +trtp_rtcp_report_sdes_t* trtp_rtcp_report_sdes_create_null(); +trtp_rtcp_report_sdes_t* trtp_rtcp_report_sdes_create(struct trtp_rtcp_header_s* header); +trtp_rtcp_report_sdes_t* trtp_rtcp_report_sdes_deserialize(const void* data, tsk_size_t size); +int trtp_rtcp_report_sdes_serialize_to(const trtp_rtcp_report_sdes_t* self, void* data, tsk_size_t size); +int trtp_rtcp_report_sdes_add_chunck(trtp_rtcp_report_sdes_t* self, trtp_rtcp_sdes_chunck_t* chunck); +tsk_size_t trtp_rtcp_report_sdes_get_size(const trtp_rtcp_report_sdes_t* self); + +TRTP_END_DECLS + +#endif /* TINYRTP_RTCP_REPORT_SDES_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_sr.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_sr.h new file mode 100644 index 0000000..09d442f --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_sr.h @@ -0,0 +1,62 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_REPORT_SR_H +#define TINYRTP_RTCP_REPORT_SR_H + +#include "tinyrtp_config.h" + +#include "tinyrtp/rtcp/trtp_rtcp_packet.h" +#include "tinyrtp/rtcp/trtp_rtcp_rblock.h" + +TRTP_BEGIN_DECLS + +#define TRTP_RTCP_REPORT_SR(self) ((trtp_rtcp_report_sr_t*)(self)) + +// RFC 3550 6.4.1 SR: Sender Report RTCP Packet +typedef struct trtp_rtcp_report_sr_s +{ + TRTP_DECLARE_RTCP_PACKET; + + uint32_t ssrc; + struct{ + uint32_t ntp_msw; /**< NTP timestamp, most significant word */ + uint32_t ntp_lsw; /**< NTP timestamp, least significant word */ + uint32_t rtp_timestamp;/**< RTP timestamp */ + uint32_t sender_pcount; /**< sender's packet count */ + uint32_t sender_ocount; /**< sender's octet count */ + } sender_info; + + trtp_rtcp_rblocks_L_t* blocks; + trtp_rtcp_packets_L_t* packets; +} +trtp_rtcp_report_sr_t; + +trtp_rtcp_report_sr_t* trtp_rtcp_report_sr_create_null(); +trtp_rtcp_report_sr_t* trtp_rtcp_report_sr_create(struct trtp_rtcp_header_s* header); +trtp_rtcp_report_sr_t* trtp_rtcp_report_sr_deserialize(const void* data, tsk_size_t size); +int trtp_rtcp_report_sr_serialize_to(const trtp_rtcp_report_sr_t* self, void* data, tsk_size_t size); +int trtp_rtcp_report_sr_add_block(trtp_rtcp_report_sr_t* self, trtp_rtcp_rblock_t* rblock); +tsk_size_t trtp_rtcp_report_sr_get_size(const trtp_rtcp_report_sr_t* self); + +TRTP_END_DECLS + +#endif /* TINYRTP_RTCP_REPORT_SR_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_xr.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_xr.h new file mode 100644 index 0000000..b08e096 --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_report_xr.h @@ -0,0 +1,32 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_REPORT_XR_H +#define TINYRTP_RTCP_REPORT_XR_H + +#include "tinyrtp_config.h" + +TRTP_BEGIN_DECLS + + +TRTP_END_DECLS + +#endif /* TINYRTP_RTCP_REPORT_XR_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_sdes_chunck.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_sdes_chunck.h new file mode 100644 index 0000000..ec3a8d8 --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_sdes_chunck.h @@ -0,0 +1,56 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_SDES_CHUNCK_H +#define TINYRTP_RTCP_SDES_CHUNCK_H + +#include "tinyrtp_config.h" + +#include "tinyrtp/rtcp/trtp_rtcp_sdes_item.h" + +#define TRTP_RTCP_SDES_CHUNCK_MIN_SIZE 4 +#define TRTP_RTCP_SDES_CHUNCK_SSRC_OR_CSRC_SIZE 4 + +#define TRTP_RTCP_SDES_CHUNCK(self) ((trtp_rtcp_sdes_chunck_t*)(self)) + +TRTP_BEGIN_DECLS + +typedef struct trtp_rtcp_sdes_chunck_s +{ + TSK_DECLARE_OBJECT; + + uint32_t ssrc; + trtp_rtcp_sdes_items_L_t* items; +} +trtp_rtcp_sdes_chunck_t; + +typedef tsk_list_t trtp_rtcp_sdes_chuncks_L_t; /**< List of @ref trtp_rtcp_sdes_item_t elements */ + +trtp_rtcp_sdes_chunck_t* trtp_rtcp_sdes_chunck_create_null(); +trtp_rtcp_sdes_chunck_t* trtp_rtcp_sdes_chunck_create(uint32_t ssrc); +trtp_rtcp_sdes_chunck_t* trtp_rtcp_sdes_chunck_deserialize(const void* data, tsk_size_t size); +int trtp_rtcp_sdes_chunck_serialize_to(const trtp_rtcp_sdes_chunck_t* self, void* data, tsk_size_t size); +int trtp_rtcp_sdes_chunck_add_item(trtp_rtcp_sdes_chunck_t* self, trtp_rtcp_sdes_item_type_t type, const void* data, uint8_t length); +tsk_size_t trtp_rtcp_sdes_chunck_get_size(const trtp_rtcp_sdes_chunck_t* self); + +TRTP_END_DECLS + +#endif /*TINYRTP_RTCP_SDES_CHUNCK_H*/ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_sdes_item.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_sdes_item.h new file mode 100644 index 0000000..8356a74 --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_sdes_item.h @@ -0,0 +1,71 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef TINYRTP_RTCP_SDES_ITEM_H +#define TINYRTP_RTCP_SDES_ITEM_H + +#include "tinyrtp_config.h" + +#include "tsk_list.h" +#include "tsk_buffer.h" + +#define TRTP_RTCP_SDES_ITEM_MIN_SIZE 2 /* Type + Length */ + +#define TRTP_RTCP_SDES_ITEM(self) ((trtp_rtcp_sdes_item_t*)(self)) + +TRTP_BEGIN_DECLS + +// RFC 3550 12.2 SDES Types +typedef enum trtp_rtcp_sdes_item_type_e +{ + trtp_rtcp_sdes_item_type_end = 0, /**< end of SDES list */ + trtp_rtcp_sdes_item_type_cname = 1, /**< canonical name*/ + trtp_rtcp_sdes_item_type_name = 2, /**< user name */ + trtp_rtcp_sdes_item_type_email = 3, /**< user's electronic mail address*/ + trtp_rtcp_sdes_item_type_phone = 4, /**< user's phone number */ + trtp_rtcp_sdes_item_type_loc = 5, /**< geographic user location*/ + trtp_rtcp_sdes_item_type_tool = 6, /**< name of application or tool*/ + trtp_rtcp_sdes_item_type_note = 7, /**< notice about the source*/ + trtp_rtcp_sdes_item_type_priv = 8, /**< private extensions*/ +} +trtp_rtcp_sdes_item_type_t; + +typedef struct trtp_rtcp_sdes_item_s +{ + TSK_DECLARE_OBJECT; + + trtp_rtcp_sdes_item_type_t type; + tsk_buffer_t *data; +} +trtp_rtcp_sdes_item_t; + + +typedef tsk_list_t trtp_rtcp_sdes_items_L_t; /**< List of @ref trtp_rtcp_sdes_item_t elements */ + +trtp_rtcp_sdes_item_t* trtp_rtcp_sdes_item_create(trtp_rtcp_sdes_item_type_t type, const void* data, uint8_t length); +trtp_rtcp_sdes_item_t* trtp_rtcp_sdes_item_deserialize(const void* data, tsk_size_t size); +tsk_buffer_t* trtp_rtcp_sdes_item_serialize(const trtp_rtcp_sdes_item_t* self); +int trtp_rtcp_sdes_item_serialize_to(const trtp_rtcp_sdes_item_t* self, void* data, tsk_size_t size); +tsk_size_t trtp_rtcp_sdes_item_get_size(const trtp_rtcp_sdes_item_t* self); + +TRTP_END_DECLS + +#endif /* TINYRTP_RTCP_SDES_ITEM_H */ diff --git a/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_session.h b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_session.h new file mode 100644 index 0000000..34ddbbc --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtcp/trtp_rtcp_session.h @@ -0,0 +1,70 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp_rtcp_session.h + * @brief RTCP session. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + + */ +#ifndef TINYMEDIA_RTCP_SESSION_H +#define TINYMEDIA_RTCP_SESSION_H + +#include "tinyrtp_config.h" +#include "tinyrtp/trtp_srtp.h" + +#include "tnet_types.h" + +#include "tsk_common.h" + +TRTP_BEGIN_DECLS + +struct trtp_rtcp_packet_s; +struct trtp_rtp_packet_s; +struct tnet_ice_ctx_s; +struct tnet_transport_s; + +typedef int (*trtp_rtcp_cb_f)(const void* callback_data, const struct trtp_rtcp_packet_s* packet); + +struct trtp_rtcp_session_s* trtp_rtcp_session_create(uint32_t ssrc, const char* cname); +struct trtp_rtcp_session_s* trtp_rtcp_session_create_2(struct tnet_ice_ctx_s* ice_ctx, uint32_t ssrc, const char* cname); +int trtp_rtcp_session_set_callback(struct trtp_rtcp_session_s* self, trtp_rtcp_cb_f callback, const void* callback_data); +#if HAVE_SRTP +int trtp_rtcp_session_set_srtp_sess(struct trtp_rtcp_session_s* self, const srtp_t* session); +#endif +int trtp_rtcp_session_set_app_bandwidth_max(struct trtp_rtcp_session_s* self, int32_t bw_upload_kbps, int32_t bw_download_kbps); +int trtp_rtcp_session_start(struct trtp_rtcp_session_s* self, tnet_fd_t local_fd, const struct sockaddr* remote_addr); +int trtp_rtcp_session_stop(struct trtp_rtcp_session_s* self); +int trtp_rtcp_session_process_rtp_out(struct trtp_rtcp_session_s* self, const struct trtp_rtp_packet_s* packet_rtp, tsk_size_t size); +int trtp_rtcp_session_process_rtp_in(struct trtp_rtcp_session_s* self, const struct trtp_rtp_packet_s* packet_rtp, tsk_size_t size); +int trtp_rtcp_session_process_rtcp_in(struct trtp_rtcp_session_s* self, const void* buffer, tsk_size_t size); +int trtp_rtcp_session_signal_pkt_loss(struct trtp_rtcp_session_s* self, uint32_t ssrc_media, const uint16_t* seq_nums, tsk_size_t count); +int trtp_rtcp_session_signal_frame_corrupted(struct trtp_rtcp_session_s* self, uint32_t ssrc_media); +int trtp_rtcp_session_signal_jb_error(struct trtp_rtcp_session_s* self, uint32_t ssrc_media); + +tnet_fd_t trtp_rtcp_session_get_local_fd(const struct trtp_rtcp_session_s* self); +int trtp_rtcp_session_set_local_fd(struct trtp_rtcp_session_s* self, tnet_fd_t local_fd); +int trtp_rtcp_session_set_net_transport(struct trtp_rtcp_session_s* self, struct tnet_transport_s* transport); + +TRTP_END_DECLS + +#endif /* TINYMEDIA_RTCP_SESSION_H */ diff --git a/tinyRTP/include/tinyrtp/rtp/trtp_rtp_header.h b/tinyRTP/include/tinyrtp/rtp/trtp_rtp_header.h new file mode 100644 index 0000000..15d177c --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtp/trtp_rtp_header.h @@ -0,0 +1,85 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp_rtp_header.h + * @brief RTP header. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + */ +#ifndef TINYRTP_RTP_HEADER_H +#define TINYRTP_RTP_HEADER_H + +#include "tinyrtp_config.h" +#include "tinymedia/tmedia_codec.h" +#include "tsk_buffer.h" + +TRTP_BEGIN_DECLS + +#define TRTP_RTP_HEADER_MIN_SIZE 12 +#define TRTP_RTP_HEADER(self) ((trtp_rtp_header_t*)(self)) + +typedef struct trtp_rtp_header_s +{ + TSK_DECLARE_OBJECT; + /* RFC 3550 section 5.1 - RTP Fixed Header Fields + 0 1 2 3 + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P|X| CC |M| PT | sequence number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | synchronization source (SSRC) identifier | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | contributing source (CSRC) identifiers | + | .... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + unsigned version:2; + unsigned padding:1; + unsigned extension:1; + unsigned csrc_count:4; + unsigned marker:1; + unsigned payload_type:7; + uint16_t seq_num; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[15]; + + // for internal use + enum tmedia_codec_id_e codec_id; +} +trtp_rtp_header_t; + +TINYRTP_API trtp_rtp_header_t* trtp_rtp_header_create_null(); +TINYRTP_API trtp_rtp_header_t* trtp_rtp_header_create(uint32_t ssrc, uint16_t seq_num, uint32_t timestamp, uint8_t payload_type, tsk_bool_t marker); +TINYRTP_API tsk_size_t trtp_rtp_header_guess_serialbuff_size(const trtp_rtp_header_t *self); +TINYRTP_API tsk_size_t trtp_rtp_header_serialize_to(const trtp_rtp_header_t *self, void *buffer, tsk_size_t size); +TINYRTP_API tsk_buffer_t* trtp_rtp_header_serialize(const trtp_rtp_header_t *self); +TINYRTP_API trtp_rtp_header_t* trtp_rtp_header_deserialize(const void *data, tsk_size_t size); + + +TINYRTP_GEXTERN const tsk_object_def_t *trtp_rtp_header_def_t; + +TRTP_END_DECLS + +#endif /* TINYRTP_RTP_HEADER_H */ diff --git a/tinyRTP/include/tinyrtp/rtp/trtp_rtp_packet.h b/tinyRTP/include/tinyrtp/rtp/trtp_rtp_packet.h new file mode 100644 index 0000000..c7f8cf3 --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtp/trtp_rtp_packet.h @@ -0,0 +1,74 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp_rtp_packet.h + * @brief RTP packet. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + */ +#ifndef TINYRTP_RTP_PACKET_H +#define TINYRTP_RTP_PACKET_H + +#include "tinyrtp_config.h" + +#include "tinyrtp/rtp/trtp_rtp_header.h" + +#include "tsk_object.h" + +TRTP_BEGIN_DECLS + + +typedef struct trtp_rtp_packet_s +{ + TSK_DECLARE_OBJECT; + + trtp_rtp_header_t* header; + + struct{ + void* data; + const void* data_const; // never free()d. an alternative to "data" + tsk_size_t size; + } payload; + + /* extension header as per RFC 3550 section 5.3.1 */ + struct{ + void* data; + tsk_size_t size; /* contains the first two 16-bit fields */ + } extension; +} +trtp_rtp_packet_t; +typedef tsk_list_t trtp_rtp_packets_L_t; + +TINYRTP_API trtp_rtp_packet_t* trtp_rtp_packet_create_null(); +TINYRTP_API trtp_rtp_packet_t* trtp_rtp_packet_create(uint32_t ssrc, uint16_t seq_num, uint32_t timestamp, uint8_t payload_type, tsk_bool_t marker); +TINYRTP_API trtp_rtp_packet_t* trtp_rtp_packet_create_2(const trtp_rtp_header_t* header); +TINYRTP_API tsk_size_t trtp_rtp_packet_guess_serialbuff_size(const trtp_rtp_packet_t *self); +TINYRTP_API tsk_size_t trtp_rtp_packet_serialize_to(const trtp_rtp_packet_t *self, void* buffer, tsk_size_t size); +TINYRTP_API tsk_buffer_t* trtp_rtp_packet_serialize(const trtp_rtp_packet_t *self, tsk_size_t num_bytes_pad); +TINYRTP_API trtp_rtp_packet_t* trtp_rtp_packet_deserialize(const void *data, tsk_size_t size); + + +TINYRTP_GEXTERN const tsk_object_def_t *trtp_rtp_packet_def_t; + +TRTP_END_DECLS + +#endif /* TINYRTP_RTP_PACKET_H */ diff --git a/tinyRTP/include/tinyrtp/rtp/trtp_rtp_session.h b/tinyRTP/include/tinyrtp/rtp/trtp_rtp_session.h new file mode 100644 index 0000000..475502a --- /dev/null +++ b/tinyRTP/include/tinyrtp/rtp/trtp_rtp_session.h @@ -0,0 +1,42 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp_rtp_session.h + * @brief RTP session. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + + */ +#ifndef TINYMEDIA_RTP_SESSION_H +#define TINYMEDIA_RTP_SESSION_H + +#include "tinyrtp_config.h" + +TRTP_BEGIN_DECLS + +struct trtp_rtp_packet_s; + +typedef int (*trtp_rtp_cb_f)(const void* callback_data, const struct trtp_rtp_packet_s* packet); + +TRTP_END_DECLS + +#endif /* TINYMEDIA_RTP_SESSION_H */ diff --git a/tinyRTP/include/tinyrtp/trtp.h b/tinyRTP/include/tinyrtp/trtp.h new file mode 100644 index 0000000..3359a70 --- /dev/null +++ b/tinyRTP/include/tinyrtp/trtp.h @@ -0,0 +1,38 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp.h + * @brief tinyRTP (Real-time Transport Protocol) as per RFC 3550. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + + */ +#ifndef TINYMEDIA_TRTP_H +#define TINYMEDIA_TRTP_H + +#include "tinyrtp_config.h" + +TRTP_BEGIN_DECLS + +TRTP_END_DECLS + +#endif /* TINYMEDIA_TRTP_H */ diff --git a/tinyRTP/include/tinyrtp/trtp_manager.h b/tinyRTP/include/tinyrtp/trtp_manager.h new file mode 100644 index 0000000..1594f9c --- /dev/null +++ b/tinyRTP/include/tinyrtp/trtp_manager.h @@ -0,0 +1,227 @@ +/* +* Copyright (C) 2012 Mamadou Diop +* Copyright (C) 2012-2013 Doubango Telecom <http://www.doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp_manager.h + * @brief RTP/RTCP manager. + */ +#ifndef TINYRTP_MANAGER_H +#define TINYRTP_MANAGER_H + +#include "tinyrtp_config.h" + +#include "tinyrtp/rtp/trtp_rtp_session.h" +#include "tinyrtp/rtcp/trtp_rtcp_session.h" +#include "tinyrtp/trtp_srtp.h" + +#include "tinymedia/tmedia_defaults.h" + +#include "tinynet.h" + +TRTP_BEGIN_DECLS + +struct trtp_rtp_packet_s; +struct tnet_proxyinfo_s; + +/** RTP/RTCP manager */ +typedef struct trtp_manager_s +{ + TSK_DECLARE_OBJECT; + + char* local_ip; + tsk_bool_t use_ipv6; + tsk_bool_t is_started; + tsk_bool_t use_rtcp; + tsk_bool_t use_rtcpmux; + tsk_bool_t is_socket_disabled; + tsk_bool_t is_ice_neg_ok; + tsk_bool_t is_ice_turn_active; + tsk_bool_t is_force_symetric_rtp; + tsk_bool_t is_symetric_rtp_checked; + tsk_bool_t is_symetric_rtcp_checked; + int32_t app_bw_max_upload; // application specific (kbps) + int32_t app_bw_max_download; // application specific (kbps) + + tnet_transport_t* transport; + + struct tnet_ice_ctx_s* ice_ctx; + + tsk_timer_manager_handle_t* timer_mgr_global; + + struct{ + tmedia_rtcweb_type_t local; + tmedia_rtcweb_type_t remote; + } rtcweb_type; + + struct { + tsk_bool_t auto_detect; + struct tnet_proxyinfo_s* info; + } + proxy; + + struct{ + uint16_t start; + uint16_t stop; + } port_range; + + struct{ + uint16_t seq_num; + uint32_t timestamp; + uint8_t payload_type; + int32_t dscp; + + char* remote_ip; + tnet_port_t remote_port; + struct sockaddr_storage remote_addr; + + char* public_ip; + tnet_port_t public_port; + + struct{ + uint32_t local; + uint32_t remote; + } ssrc; + + struct{ + const void* usrdata; + trtp_rtp_cb_f fun; + } cb; + + struct{ + void* ptr; + tsk_size_t size; + tsk_size_t index; + } serial_buffer; + } rtp; + + struct{ + char* cname; + char* remote_ip; + tnet_port_t remote_port; + struct sockaddr_storage remote_addr; + tnet_socket_t* local_socket; + + char* public_ip; + tnet_port_t public_port; + + struct{ + const void* usrdata; + trtp_rtcp_cb_f fun; + } cb; + + struct trtp_rtcp_session_s* session; + } rtcp; + + TSK_DECLARE_SAFEOBJ; + +#if HAVE_SRTP + enum tmedia_srtp_type_e srtp_type; + enum tmedia_srtp_mode_e srtp_mode; + trtp_srtp_state_t srtp_state; + trtp_srtp_ctx_xt srtp_contexts[2/*LINE_IDX*/][2/*CRYPTO_TYPE*/]; + const struct trtp_srtp_ctx_xs* srtp_ctx_neg_local; + const struct trtp_srtp_ctx_xs* srtp_ctx_neg_remote; + + struct{ + char* file_ca; + char* file_pbk; + char* file_pvk; + tsk_bool_t cert_verif; + + trtp_srtp_state_t state; + // enable() could be postponed if net transport not ready yet (e.g. when ICE is ON) + tsk_bool_t enable_postponed; + + tsk_bool_t srtp_connected; + tsk_bool_t srtcp_connected; + tsk_bool_t srtp_handshake_succeed; + tsk_bool_t srtcp_handshake_succeed; + + trtp_srtp_crypto_type_t crypto_selected; + + struct{ + uint64_t timeout; + tsk_timer_id_t id; + } timer_hanshaking; + + struct{ + const void* usrdata; + trtp_srtp_dtls_cb_f fun; + } cb; + + struct{ + tnet_fingerprint_t fp; + tnet_dtls_hash_type_t fp_hash; + } remote; + struct{ + tnet_dtls_setup_t setup; + tsk_bool_t connection_new; + }local; + } dtls; +#endif +} +trtp_manager_t; + +TINYRTP_API trtp_manager_t* trtp_manager_create(tsk_bool_t use_rtcp, const char* local_ip, tsk_bool_t use_ipv6, enum tmedia_srtp_type_e srtp_type, enum tmedia_srtp_mode_e srtp_mode); +TINYRTP_API trtp_manager_t* trtp_manager_create_2(struct tnet_ice_ctx_s* ice_ctx, enum tmedia_srtp_type_e srtp_type, enum tmedia_srtp_mode_e srtp_mode); +TINYRTP_API int trtp_manager_set_ice_ctx(trtp_manager_t* self, struct tnet_ice_ctx_s* ice_ctx); +TINYRTP_API int trtp_manager_prepare(trtp_manager_t* self); +#if HAVE_SRTP +TINYRTP_API int trtp_manager_set_dtls_certs(trtp_manager_t* self, const char* ca, const char* pbk, const char* pvk, tsk_bool_t verify); +TINYRTP_API int trtp_manager_set_dtls_remote_fingerprint(trtp_manager_t* self, const tnet_fingerprint_t* fp, const char* hash); +TINYRTP_API enum tnet_dtls_hash_type_e trtp_manager_get_dtls_remote_fingerprint_hash(trtp_manager_t* self); +TINYRTP_API int trtp_manager_set_dtls_local_setup(trtp_manager_t* self, tnet_dtls_setup_t setup, tsk_bool_t connection_new); +TINYRTP_API int trtp_manager_set_dtls_callback(trtp_manager_t* self, const void* usrdata, trtp_srtp_dtls_cb_f fun); +TINYRTP_API const char* trtp_manager_get_dtls_local_fingerprint(trtp_manager_t* self, enum tnet_dtls_hash_type_e hash); +TINYRTP_API tsk_bool_t trtp_manager_is_dtls_enabled(trtp_manager_t* self); +TINYRTP_API tsk_bool_t trtp_manager_is_dtls_activated(trtp_manager_t* self); +TINYRTP_API tsk_bool_t trtp_manager_is_dtls_started(trtp_manager_t* self); +TINYRTP_API tsk_bool_t trtp_manager_is_srtp_activated(trtp_manager_t* self); +TINYRTP_API tsk_bool_t trtp_manager_is_srtp_started(trtp_manager_t* self); +TINYRTP_API int trtp_manager_set_srtp_type_remote(trtp_manager_t* self, enum tmedia_srtp_type_e srtp_type); +TINYRTP_API int trtp_manager_set_srtp_type_local(trtp_manager_t* self, enum tmedia_srtp_type_e srtp_type, enum tmedia_srtp_mode_e srtp_mode); +#endif /* HAVE_SRTP */ +TINYRTP_API tsk_bool_t trtp_manager_is_ready(trtp_manager_t* self); +TINYRTP_API int trtp_manager_set_natt_ctx(trtp_manager_t* self, struct tnet_nat_ctx_s* natt_ctx); +TINYRTP_API int trtp_manager_set_rtp_callback(trtp_manager_t* self, trtp_rtp_cb_f fun, const void* usrdata); +TINYRTP_API int trtp_manager_set_rtcp_callback(trtp_manager_t* self, trtp_rtcp_cb_f fun, const void* usrdata); +TINYRTP_API int trtp_manager_set_rtp_dscp(trtp_manager_t* self, int32_t dscp); +TINYRTP_API int trtp_manager_set_payload_type(trtp_manager_t* self, uint8_t payload_type); +TINYRTP_API int trtp_manager_set_rtp_remote(trtp_manager_t* self, const char* remote_ip, tnet_port_t remote_port); +TINYRTP_API int trtp_manager_set_rtcp_remote(trtp_manager_t* self, const char* remote_ip, tnet_port_t remote_port); +TINYRTP_API int trtp_manager_set_port_range(trtp_manager_t* self, uint16_t start, uint16_t stop); +TINYRTP_API int trtp_manager_set_rtcweb_type_remote(trtp_manager_t* self, tmedia_rtcweb_type_t rtcweb_type); +TINYRTP_API int trtp_manager_set_proxy_auto_detect(trtp_manager_t* self, tsk_bool_t auto_detect); +TINYRTP_API int trtp_manager_set_proxy_info(trtp_manager_t* self, enum tnet_proxy_type_e type, const char* host, tnet_port_t port, const char* login, const char* password); +TINYRTP_API int trtp_manager_start(trtp_manager_t* self); +TINYRTP_API tsk_size_t trtp_manager_send_rtp(trtp_manager_t* self, const void* data, tsk_size_t size, uint32_t duration, tsk_bool_t marker, tsk_bool_t last_packet); +TINYRTP_API tsk_size_t trtp_manager_send_rtp_packet(trtp_manager_t* self, const struct trtp_rtp_packet_s* packet, tsk_bool_t bypass_encrypt); +TINYRTP_API int trtp_manager_get_bytes_count(trtp_manager_t* self, uint64_t* bytes_in, uint64_t* bytes_out); +TINYRTP_API tsk_size_t trtp_manager_send_rtp_raw(trtp_manager_t* self, const void* data, tsk_size_t size); +TINYRTP_API int trtp_manager_set_app_bandwidth_max(trtp_manager_t* self, int32_t bw_upload_kbps, int32_t bw_download_kbps); +TINYRTP_API int trtp_manager_signal_pkt_loss(trtp_manager_t* self, uint32_t ssrc_media, const uint16_t* seq_nums, tsk_size_t count); +TINYRTP_API int trtp_manager_signal_frame_corrupted(trtp_manager_t* self, uint32_t ssrc_media); +TINYRTP_API int trtp_manager_signal_jb_error(trtp_manager_t* self, uint32_t ssrc_media); +TINYRTP_API int trtp_manager_stop(trtp_manager_t* self); + +TINYRTP_GEXTERN const tsk_object_def_t *trtp_manager_def_t; + +TRTP_END_DECLS + +#endif /* TINYRTP_MANAGER_H */ diff --git a/tinyRTP/include/tinyrtp/trtp_srtp.h b/tinyRTP/include/tinyrtp/trtp_srtp.h new file mode 100644 index 0000000..9992bbb --- /dev/null +++ b/tinyRTP/include/tinyrtp/trtp_srtp.h @@ -0,0 +1,131 @@ +/* +* Copyright (C) 2012 Mamadou Diop +* Copyright (C) 2012-2013 Doubango Telecom <http://www.doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp_srtp.h + */ +#ifndef TINYRTP_SRTP_H +#define TINYRTP_SRTP_H + +#include "tinyrtp_config.h" + +#if HAVE_SRTP +# include "tsk_common.h" +# include <srtp/srtp.h> + +struct trtp_manager_s; + +typedef enum trtp_srtp_dtls_event_type_e +{ + trtp_srtp_dtls_event_type_handshake_failed, + trtp_srtp_dtls_event_type_handshake_succeed, + trtp_srtp_dtls_event_type_fatal_error, + trtp_srtp_dtls_event_type_started +} +trtp_srtp_dtls_event_type_t; + +typedef enum trtp_srtp_crypto_type_e +{ + NONE = -1, + HMAC_SHA1_80, + HMAC_SHA1_32, + + SRTP_CRYPTO_TYPES_MAX +} +trtp_srtp_crypto_type_t; + +typedef enum trtp_srtp_state_e +{ + trtp_srtp_state_none, + /* at this state we're able to generated DTLS "fingerprints" and SDES "crypro" attributes + but neither encrypt() nor decrypt() is possible. + it's possible to move backward and disable SRTP (e.g. because of negotiation error) + it's required to move to this state in order to be able to negotiate SRTP when mode is "optional" or "mandatory" + */ + trtp_srtp_state_enabled, + /* at this state both required parameters (e.g. "crypto" attributes) have been successfuly proceeded + it's not possible to move backward and disable SRTP + if type="SDES": start()ing the engine means we'll be imediately able to encrypt()/decrypt() data + if type="DTLS": start()ing the engine doesn't mean we will be able to encrypt()/decrypt() data unless handshaking process successfuly completed + */ + trtp_srtp_state_activated, + /* at this state we're able to encrypt()/decrypt() SRTP data + */ + trtp_srtp_state_started +} +trtp_srtp_state_t; + +typedef int (*trtp_srtp_dtls_cb_f)(const void* usrdata, enum trtp_srtp_dtls_event_type_e type, const char* reason); + +#define TRTP_SRTP_AES_CM_128_HMAC_SHA1_80 "AES_CM_128_HMAC_SHA1_80" +#define TRTP_SRTP_AES_CM_128_HMAC_SHA1_32 "AES_CM_128_HMAC_SHA1_32" + +#define TRTP_SRTP_LINE_IDX_LOCAL 0 +#define TRTP_SRTP_LINE_IDX_REMOTE 1 + +static const char* trtp_srtp_crypto_type_strings[2] = +{ + TRTP_SRTP_AES_CM_128_HMAC_SHA1_80, TRTP_SRTP_AES_CM_128_HMAC_SHA1_32 +}; + + +typedef struct trtp_srtp_ctx_internal_xs +{ + int32_t tag; + trtp_srtp_crypto_type_t crypto_type; + char key_str[SRTP_MAX_KEY_LEN]; + char key_bin[SRTP_MASTER_KEY_LEN]; + tsk_bool_t have_valid_key; + + srtp_t session; + srtp_policy_t policy; + tsk_bool_t initialized; +} +trtp_srtp_ctx_internal_xt; + +typedef struct trtp_srtp_ctx_xs +{ + // (rtp == rtcp) for SDES but different for DTLS + struct trtp_srtp_ctx_internal_xs rtp; + struct trtp_srtp_ctx_internal_xs rtcp; +} +trtp_srtp_ctx_xt; + +int trtp_srtp_ctx_internal_init(struct trtp_srtp_ctx_internal_xs* ctx, int32_t tag, trtp_srtp_crypto_type_t type, uint32_t ssrc); +int trtp_srtp_ctx_internal_deinit(struct trtp_srtp_ctx_internal_xs* ctx); +int trtp_srtp_ctx_init(struct trtp_srtp_ctx_xs* ctx, int32_t tag, trtp_srtp_crypto_type_t type, uint32_t ssrc); +int trtp_srtp_ctx_deinit(struct trtp_srtp_ctx_xs* ctx); +TINYRTP_API int trtp_srtp_match_line(const char* crypto_line, int32_t* tag, int32_t* crypto_type, char* key, tsk_size_t key_size); + +TINYRTP_API int trtp_srtp_set_crypto(struct trtp_manager_s* rtp_mgr, const char* crypto_line, int32_t idx); +#define trtp_srtp_set_crypto_local(rtp_mgr, crypto_line) trtp_srtp_set_crypto((rtp_mgr), (crypto_line), TRTP_SRTP_LINE_IDX_LOCAL) +#define trtp_srtp_set_crypto_remote(rtp_mgr, crypto_line) trtp_srtp_set_crypto((rtp_mgr), (crypto_line), TRTP_SRTP_LINE_IDX_REMOTE) +TINYRTP_API int trtp_srtp_set_key_and_salt(struct trtp_manager_s* rtp_mgr, trtp_srtp_crypto_type_t crypto_type, const void* key, tsk_size_t key_size, const void* salt, tsk_size_t salt_size, int32_t idx, tsk_bool_t rtp); +#define trtp_srtp_set_key_and_salt_local(rtp_mgr, crypto_type, key, key_size, salt, salt_size, is_rtp) trtp_srtp_set_key_and_salt((rtp_mgr), (crypto_type), (key), (key_size), (salt), (salt_size), TRTP_SRTP_LINE_IDX_LOCAL, (is_rtp)) +#define trtp_srtp_set_key_and_salt_remote(rtp_mgr, crypto_type, key, key_size, salt, salt_size, is_rtp) trtp_srtp_set_key_and_salt((rtp_mgr), (crypto_type), (key), (key_size), (salt), (salt_size), TRTP_SRTP_LINE_IDX_REMOTE, (is_rtp)) +TINYRTP_API tsk_size_t trtp_srtp_get_local_contexts(struct trtp_manager_s* rtp_mgr, const struct trtp_srtp_ctx_xs ** contexts, tsk_size_t contexts_count); +TINYRTP_API tsk_bool_t trtp_srtp_is_initialized(struct trtp_manager_s* rtp_mgr); +TINYRTP_API tsk_bool_t trtp_srtp_is_started(struct trtp_manager_s* rtp_mgr); + +#endif /* HAVE_SRTP */ + + + + +#endif /* TINYRTP_SRTP_H */ diff --git a/tinyRTP/include/tinyrtp_config.h b/tinyRTP/include/tinyrtp_config.h new file mode 100644 index 0000000..422b68f --- /dev/null +++ b/tinyRTP/include/tinyrtp_config.h @@ -0,0 +1,92 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ + +#ifndef TINYRTP_CONFIG_H +#define TINYRTP_CONFIG_H + +#ifdef __SYMBIAN32__ +#undef _WIN32 /* Because of WINSCW */ +#endif + +// Windows (XP/Vista/7/CE and Windows Mobile) macro definition. +#if defined(WIN32)|| defined(_WIN32) || defined(_WIN32_WCE) +# define TRTP_UNDER_WINDOWS 1 +# if defined(_WIN32_WCE) || defined(UNDER_CE) +# define TRTP_UNDER_WINDOWS_CE 1 +# endif +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define TRTP_UNDER_WINDOWS_RT 1 +# endif +#endif + +#if (TRTP_UNDER_WINDOWS || defined(__SYMBIAN32__)) && defined(TINYRTP_EXPORTS) +# define TINYRTP_API __declspec(dllexport) +# define TINYRTP_GEXTERN __declspec(dllexport) +#elif (TRTP_UNDER_WINDOWS || defined(__SYMBIAN32__)) && !defined(TINYRTP_IMPORTS_IGNORE) +# define TINYRTP_API __declspec(dllimport) +# define TINYRTP_GEXTERN __declspec(dllimport) +#else +# define TINYRTP_API +# define TINYRTP_GEXTERN extern +#endif + +/* Guards against C++ name mangling +*/ +#ifdef __cplusplus +# define TRTP_BEGIN_DECLS extern "C" { +# define TRTP_END_DECLS } +#else +# define TRTP_BEGIN_DECLS +# define TRTP_END_DECLS +#endif + +/* Disable some well-known warnings +*/ +#ifdef _MSC_VER +# define _CRT_SECURE_NO_WARNINGS +#endif + +/* Detecting C99 compilers + */ +#if (__STDC_VERSION__ == 199901L) && !defined(__C99__) +# define __C99__ +#endif + +#if !defined(TRTP_RTP_VERSION) +# define TRTP_RTP_VERSION 2 +#endif /* TRTP_RTP_VERSION */ + +#include <stdint.h> +#ifdef __SYMBIAN32__ +# include <stdlib.h> +#endif + +#if defined(__APPLE__) +# include <TargetConditionals.h> +# include <Availability.h> +#endif + +#if HAVE_CONFIG_H + #include <config.h> +#endif + +#endif // TINYRTP_CONFIG_H diff --git a/tinyRTP/src/rtcp/trtp_rtcp_header.c b/tinyRTP/src/rtcp/trtp_rtcp_header.c new file mode 100644 index 0000000..e93772a --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_header.c @@ -0,0 +1,134 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinyrtp/rtcp/trtp_rtcp_header.h" + +#include "tnet_endianness.h" + +#include "tsk_memory.h" +#include "tsk_debug.h" +/* + 0 1 2 3 + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| RC | PT=SR=200 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +trtp_rtcp_header_t* trtp_rtcp_header_create_null() +{ + return tsk_object_new(trtp_rtcp_header_def_t); +} + +trtp_rtcp_header_t* trtp_rtcp_header_create(uint8_t version, uint8_t padding, uint8_t rc, trtp_rtcp_packet_type_t type, uint16_t length_in_bytes) +{ + trtp_rtcp_header_t* header; + if((header = trtp_rtcp_header_create_null())){ + header->version = version; + header->padding = padding; + header->rc = rc; + header->type = type; + header->length_in_words_minus1 = ((length_in_bytes >> 2) - 1); + header->length_in_bytes = length_in_bytes; + } + + return header; +} + +int trtp_rtcp_header_serialize_to(const trtp_rtcp_header_t *self, void* data, tsk_size_t size) +{ + uint8_t* pdata = (uint8_t*)data; + if(!self || !data || size < TRTP_RTCP_HEADER_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + // Octet-0: version(2), Padding(1), RC(5) + pdata[0] = ((uint8_t)self->version)<<6 | + ((uint8_t)self->padding)<<5 | + ((uint8_t)self->rc); + // Octet-1: PT(8) + pdata[1] = self->type; + // Octet-2 and Octet3: length (16) + pdata[2] = self->length_in_words_minus1 >> 8; + pdata[3] = self->length_in_words_minus1 & 0xFF; + return 0; +} + +trtp_rtcp_header_t* trtp_rtcp_header_deserialize(const void *data, tsk_size_t size) +{ + trtp_rtcp_header_t* header = tsk_null; + if(trtp_rtcp_header_deserialize_to(&header, data, size) != 0){ + TSK_DEBUG_ERROR("Failed to deserialize the rtcp header"); + TSK_OBJECT_SAFE_FREE(header); + } + return header; +} + +int trtp_rtcp_header_deserialize_to(trtp_rtcp_header_t** self, const void *data, tsk_size_t size) +{ + const uint8_t* pdata = (uint8_t*)data; + if(!self || !data || size < TRTP_RTCP_HEADER_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(!*self && !(*self = trtp_rtcp_header_create_null())){ + TSK_DEBUG_ERROR("Failed to create new rtcp header"); + return -3; + } + (*self)->version = pdata[0] >> 6; + (*self)->padding = (pdata[0] >> 5) & 0x01; + (*self)->rc = (pdata[0] & 0x1f); + (*self)->type = (enum trtp_rtcp_packet_type_e)pdata[1]; + (*self)->length_in_words_minus1 = tnet_ntohs_2(&pdata[2]); + (*self)->length_in_bytes = ((*self)->length_in_words_minus1 + 1) << 2; + + return 0; +} + +//================================================================================================= +// RTCP header object definition +// +static tsk_object_t* trtp_rtcp_header_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_header_t *header = self; + if(header){ + } + return self; +} + +static tsk_object_t* trtp_rtcp_header_dtor(tsk_object_t * self) +{ + trtp_rtcp_header_t *header = self; + if(header){ + } + + return self; +} + +static const tsk_object_def_t trtp_rtcp_header_def_s = +{ + sizeof(trtp_rtcp_header_t), + trtp_rtcp_header_ctor, + trtp_rtcp_header_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_header_def_t = &trtp_rtcp_header_def_s; diff --git a/tinyRTP/src/rtcp/trtp_rtcp_packet.c b/tinyRTP/src/rtcp/trtp_rtcp_packet.c new file mode 100644 index 0000000..d2ec856 --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_packet.c @@ -0,0 +1,255 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinyrtp/rtcp/trtp_rtcp_packet.h" + +#include "tinyrtp/rtcp/trtp_rtcp_header.h" +#include "tinyrtp/rtcp/trtp_rtcp_report_sdes.h" +#include "tinyrtp/rtcp/trtp_rtcp_report_sr.h" +#include "tinyrtp/rtcp/trtp_rtcp_report_rr.h" +#include "tinyrtp/rtcp/trtp_rtcp_report_bye.h" +#include "tinyrtp/rtcp/trtp_rtcp_report_fb.h" + +#include "tsk_debug.h" + +static tsk_object_t* trtp_rtcp_packet_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_packet_t *packet = self; + if(packet){ + } + return self; +} + +static tsk_object_t* trtp_rtcp_packet_dtor(tsk_object_t * self) +{ + trtp_rtcp_packet_t *packet = self; + if(packet){ + + } + + return self; +} + +static const tsk_object_def_t trtp_rtcp_packet_def_s = +{ + sizeof(trtp_rtcp_packet_t), + trtp_rtcp_packet_ctor, + trtp_rtcp_packet_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_packet_def_t = &trtp_rtcp_packet_def_s; + +trtp_rtcp_packet_t* trtp_rtcp_packet_create(struct trtp_rtcp_header_s* header) +{ + trtp_rtcp_packet_t* packet; + if((packet = tsk_object_new(trtp_rtcp_packet_def_t)) && header){ + packet->header = tsk_object_ref(header); + } + return packet; +} + + +int trtp_rtcp_packet_init(trtp_rtcp_packet_t* self, uint8_t version, uint8_t padding, uint8_t rc, trtp_rtcp_packet_type_t type, uint16_t length_in_bytes) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if(!self->header){ + self->header = trtp_rtcp_header_create(version, padding, rc, type, length_in_bytes); + } + else{ + self->header->version = version; + self->header->padding = padding; + self->header->rc = rc; + self->header->type = type; + self->header->length_in_bytes = length_in_bytes; + self->header->length_in_words_minus1 = ((length_in_bytes >> 2) - 1); + } + return 0; +} + +trtp_rtcp_packet_t* trtp_rtcp_packet_deserialize(const void* data, tsk_size_t size) +{ + trtp_rtcp_packet_type_t type; + + if(!data || size < TRTP_RTCP_HEADER_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + type = (trtp_rtcp_packet_type_t)((const uint8_t*)data)[1]; + + switch(type){ + case trtp_rtcp_packet_type_rr: return (trtp_rtcp_packet_t*)trtp_rtcp_report_rr_deserialize(data, size); + case trtp_rtcp_packet_type_sr: return (trtp_rtcp_packet_t*)trtp_rtcp_report_sr_deserialize(data, size); + case trtp_rtcp_packet_type_sdes: return (trtp_rtcp_packet_t*)trtp_rtcp_report_sdes_deserialize(data, size); + case trtp_rtcp_packet_type_bye: return (trtp_rtcp_packet_t*)trtp_rtcp_report_bye_deserialize(data, size); + case trtp_rtcp_packet_type_rtpfb: return (trtp_rtcp_packet_t*)trtp_rtcp_report_rtpfb_deserialize(data, size); + case trtp_rtcp_packet_type_psfb: return (trtp_rtcp_packet_t*)trtp_rtcp_report_psfb_deserialize(data, size); + default: + { + // returns abstract RTCP packet + trtp_rtcp_header_t* header; + TSK_DEBUG_ERROR("%d not recognized as valid RTCP packet type", (int)type); + if((header = trtp_rtcp_header_deserialize(data, size))){ + trtp_rtcp_packet_t* packet = trtp_rtcp_packet_create(header); + TSK_OBJECT_SAFE_FREE(header); + return packet; + } + break; + } + } + + return tsk_null; +} + +int trtp_rtcp_packet_serialize_to(const trtp_rtcp_packet_t* self, void* data, tsk_size_t size) +{ + if(!self || !self->header || !data || !size){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + switch(self->header->type){ + case trtp_rtcp_packet_type_rr: return trtp_rtcp_report_rr_serialize_to((const trtp_rtcp_report_rr_t*)self, data, size); + case trtp_rtcp_packet_type_sr: return trtp_rtcp_report_sr_serialize_to((const trtp_rtcp_report_sr_t*)self, data, size); + case trtp_rtcp_packet_type_sdes: return trtp_rtcp_report_sdes_serialize_to((const trtp_rtcp_report_sdes_t*)self, data, size); + case trtp_rtcp_packet_type_bye: return trtp_rtcp_report_bye_serialize_to((const trtp_rtcp_report_bye_t*)self, data, size); + case trtp_rtcp_packet_type_psfb: return trtp_rtcp_report_psfb_serialize_to((const trtp_rtcp_report_psfb_t*)self, data, size); + case trtp_rtcp_packet_type_rtpfb: return trtp_rtcp_report_rtpfb_serialize_to((const trtp_rtcp_report_rtpfb_t*)self, data, size); + default: + { + TSK_DEBUG_ERROR("%d not recognized as valid RTCP packet type", (int)self->header->type); + return -2; + } + } +} + +tsk_buffer_t* trtp_rtcp_packet_serialize(const trtp_rtcp_packet_t* self, tsk_size_t num_bytes_pad) +{ + tsk_size_t size = trtp_rtcp_packet_get_size(self); + if(self && size){ + tsk_buffer_t* buffer; + const tsk_size_t _size = (size + num_bytes_pad); + if((buffer = tsk_buffer_create(tsk_null, _size))){ + if(trtp_rtcp_packet_serialize_to(self, buffer->data, size) != 0){ + TSK_OBJECT_SAFE_FREE(buffer); + } + else buffer->size = size; + return buffer; + } + } + return tsk_null; +} + +int trtp_rtcp_packet_add_packet(trtp_rtcp_packet_t* self, trtp_rtcp_packet_t* packet, tsk_bool_t front) +{ + trtp_rtcp_packets_L_t* packets = tsk_null; + if(!self || !self->header || !packet){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + switch(self->header->type){ + case trtp_rtcp_packet_type_rr: packets = ((trtp_rtcp_report_rr_t*)self)->packets; break; + case trtp_rtcp_packet_type_sr: packets = ((trtp_rtcp_report_sr_t*)self)->packets; break; + case trtp_rtcp_packet_type_bye: packets = ((trtp_rtcp_report_bye_t*)self)->packets; break; + default: TSK_DEBUG_ERROR("not valid operation for packet type %d", (int)self->header->type); return -2; + } + + if(packets){ + //tsk_size_t packet_size = trtp_rtcp_packet_get_size(packet); + packet = tsk_object_ref(packet); + // self->header->length_in_bytes += packet_size; + // self->header->length_in_words_minus1 = ((self->header->length_in_bytes >> 2) - 1) + + // ((self->header->length_in_bytes & 0x03) ? 1 : 0); + tsk_list_push_data(packets, (void**)&packet, !front); + } + + return 0; +} + +const trtp_rtcp_packet_t* trtp_rtcp_packet_get_at(const trtp_rtcp_packet_t* self, trtp_rtcp_packet_type_t type, tsk_size_t index) +{ + const tsk_list_item_t *item; + const trtp_rtcp_packets_L_t* packets = tsk_null; + tsk_size_t i; + + if(!self || !self->header){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + if(self->header->type == type && index == 0){ + return self; + } + switch(self->header->type){ + case trtp_rtcp_packet_type_rr: packets = ((const trtp_rtcp_report_rr_t*)self)->packets; break; + case trtp_rtcp_packet_type_sr: packets = ((const trtp_rtcp_report_sr_t*)self)->packets; break; + case trtp_rtcp_packet_type_bye: packets = ((const trtp_rtcp_report_bye_t*)self)->packets; break; + default: break; + } + + i = 0; + tsk_list_foreach(item, packets){ + if(TRTP_RTCP_PACKET(item->data)->header->type == type && i++ >= index){ + return TRTP_RTCP_PACKET(item->data); + } + } + return tsk_null; +} + +const trtp_rtcp_packet_t* trtp_rtcp_packet_get(const trtp_rtcp_packet_t* self, trtp_rtcp_packet_type_t type) +{ + return trtp_rtcp_packet_get_at(self, type, 0); +} + +tsk_size_t trtp_rtcp_packet_get_size(const trtp_rtcp_packet_t* self) +{ + if(!self || !self->header){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + + switch(self->header->type){ + case trtp_rtcp_packet_type_rr: return trtp_rtcp_report_rr_get_size((const trtp_rtcp_report_rr_t*)self); + case trtp_rtcp_packet_type_sr: return trtp_rtcp_report_sr_get_size((const trtp_rtcp_report_sr_t*)self); + case trtp_rtcp_packet_type_sdes: return trtp_rtcp_report_sdes_get_size((const trtp_rtcp_report_sdes_t*)self); + case trtp_rtcp_packet_type_bye: return trtp_rtcp_report_bye_get_size((const trtp_rtcp_report_bye_t*)self); + case trtp_rtcp_packet_type_rtpfb: return trtp_rtcp_report_rtpfb_get_size((const trtp_rtcp_report_rtpfb_t*)self); + case trtp_rtcp_packet_type_psfb: return trtp_rtcp_report_psfb_get_size((const trtp_rtcp_report_psfb_t*)self); + default: + { + TSK_DEBUG_ERROR("%d not recognized as valid RTCP packet type", (int)self->header->type); + return self->header->length_in_bytes; + } + } +} + +int trtp_rtcp_packet_deinit(trtp_rtcp_packet_t* self) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + TSK_OBJECT_SAFE_FREE(self->header); + return 0; +} diff --git a/tinyRTP/src/rtcp/trtp_rtcp_rblock.c b/tinyRTP/src/rtcp/trtp_rtcp_rblock.c new file mode 100644 index 0000000..9a85b4b --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_rblock.c @@ -0,0 +1,153 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinyrtp/rtcp/trtp_rtcp_rblock.h" + +#include "tnet_endianness.h" +#include "tsk_debug.h" + +/* 6.4.1 SR: Sender Report RTCP Packet + + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_1 (SSRC of first source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 1 | fraction lost | cumulative number of packets lost | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | extended highest sequence number received | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | interarrival jitter | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | last SR (LSR) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | delay since last SR (DLSR) | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +*/ +static tsk_object_t* trtp_rtcp_rblock_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_rblock_t *block = self; + if(block){ + } + return self; +} +static tsk_object_t* trtp_rtcp_rblock_dtor(tsk_object_t * self) +{ + trtp_rtcp_rblock_t *block = self; + if(block){ + } + + return self; +} +static const tsk_object_def_t trtp_rtcp_rblock_def_s = +{ + sizeof(trtp_rtcp_rblock_t), + trtp_rtcp_rblock_ctor, + trtp_rtcp_rblock_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_rblock_def_t = &trtp_rtcp_rblock_def_s; + +trtp_rtcp_rblock_t* trtp_rtcp_rblock_create_null() +{ + return tsk_object_new(trtp_rtcp_rblock_def_t); +} + +trtp_rtcp_rblock_t* trtp_rtcp_rblock_deserialize(const void* data, tsk_size_t size) +{ + trtp_rtcp_rblock_t* rblock = tsk_null; + const uint8_t* pdata = (const uint8_t*)data; + if(!data || size < TRTP_RTCP_RBLOCK_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + if((rblock = trtp_rtcp_rblock_create_null())){ + rblock->ssrc = (uint32_t)tnet_ntohl_2(pdata); + rblock->fraction = pdata[4]; + rblock->cumulative_no_lost = (tnet_ntohl_2(&pdata[5]) >> 8) & 0xFFFFFF; + rblock->last_seq = (uint32_t)tnet_ntohl_2(&pdata[8]); + rblock->jitter = (uint32_t)tnet_ntohl_2(&pdata[12]); + rblock->lsr = (uint32_t)tnet_ntohl_2(&pdata[16]); + rblock->dlsr = (uint32_t)tnet_ntohl_2(&pdata[20]); + } + else{ + TSK_DEBUG_ERROR("Failed to create report block object"); + } + + return rblock; +} + +// Up to the +int trtp_rtcp_rblock_deserialize_list(const void* data, tsk_size_t _size, trtp_rtcp_rblocks_L_t* dest_list) +{ + int32_t size = (int32_t)_size; + const uint8_t* pdata = (const uint8_t*)data; + trtp_rtcp_rblock_t* rblock; + + if(!data || !size || !dest_list){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + while(size >= TRTP_RTCP_RBLOCK_SIZE){ + if((rblock = trtp_rtcp_rblock_deserialize(pdata, size))){ + tsk_list_push_back_data(dest_list, (void**)&rblock); + } + if((size -= TRTP_RTCP_RBLOCK_SIZE) > 0){ + pdata += TRTP_RTCP_RBLOCK_SIZE; + } + } + return 0; +} + +int trtp_rtcp_rblock_serialize_to(const trtp_rtcp_rblock_t* self, void* data, tsk_size_t size) +{ + uint8_t* pdata = (uint8_t*)data; + if(!self || !data || size < TRTP_RTCP_RBLOCK_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + pdata[0] = self->ssrc >> 24; + pdata[1] = (self->ssrc >> 16) & 0xFF; + pdata[2] = (self->ssrc >> 8) & 0xFF; + pdata[3] = (self->ssrc & 0xFF); + pdata[4] = self->fraction; + pdata[5] = (self->cumulative_no_lost >> 16) & 0xFF; + pdata[6] = (self->cumulative_no_lost >> 8) & 0xFF; + pdata[7] = (self->cumulative_no_lost & 0xFF); + pdata[8] = self->last_seq >> 24; + pdata[9] = (self->last_seq >> 16) & 0xFF; + pdata[10] = (self->last_seq >> 8) & 0xFF; + pdata[11] = (self->last_seq & 0xFF); + pdata[12] = self->jitter >> 24; + pdata[13] = (self->jitter >> 16) & 0xFF; + pdata[14] = (self->jitter >> 8) & 0xFF; + pdata[15] = (self->jitter & 0xFF); + pdata[16] = self->lsr >> 24; + pdata[17] = (self->lsr >> 16) & 0xFF; + pdata[18] = (self->lsr >> 8) & 0xFF; + pdata[19] = (self->lsr & 0xFF); + pdata[20] = self->dlsr >> 24; + pdata[21] = (self->dlsr >> 16) & 0xFF; + pdata[22] = (self->dlsr >> 8) & 0xFF; + pdata[23] = (self->dlsr & 0xFF); + + return 0; +}
\ No newline at end of file diff --git a/tinyRTP/src/rtcp/trtp_rtcp_report.c b/tinyRTP/src/rtcp/trtp_rtcp_report.c new file mode 100644 index 0000000..5fb2697 --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_report.c @@ -0,0 +1,21 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/
\ No newline at end of file diff --git a/tinyRTP/src/rtcp/trtp_rtcp_report_bye.c b/tinyRTP/src/rtcp/trtp_rtcp_report_bye.c new file mode 100644 index 0000000..0f44608 --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_report_bye.c @@ -0,0 +1,225 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinyrtp/rtcp/trtp_rtcp_report_bye.h" +#include "tinyrtp/rtcp/trtp_rtcp_header.h" + +#include "tnet_endianness.h" + +#include "tsk_memory.h" +#include "tsk_debug.h" + +#define TRTP_RTCP_PACKET_BYE_MIN_SIZE (TRTP_RTCP_HEADER_SIZE + 0/* could have zero ssrc */) + +static tsk_object_t* trtp_rtcp_report_bye_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_report_bye_t *bye = self; + if(bye){ + bye->packets = tsk_list_create(); + } + return self; +} + +static tsk_object_t* trtp_rtcp_report_bye_dtor(tsk_object_t * self) +{ + trtp_rtcp_report_bye_t *bye = self; + if(bye){ + // deinit self + TSK_OBJECT_SAFE_FREE(bye->packets); + TSK_FREE(bye->ssrc_list); + // deinit base + trtp_rtcp_packet_deinit(TRTP_RTCP_PACKET(bye)); + } + + return self; +} + +static const tsk_object_def_t trtp_rtcp_report_bye_def_s = +{ + sizeof(trtp_rtcp_report_bye_t), + trtp_rtcp_report_bye_ctor, + trtp_rtcp_report_bye_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_report_bye_def_t = &trtp_rtcp_report_bye_def_s; + +trtp_rtcp_report_bye_t* trtp_rtcp_report_bye_create_null() +{ + trtp_rtcp_report_bye_t* bye; + if((bye = (trtp_rtcp_report_bye_t*)tsk_object_new(trtp_rtcp_report_bye_def_t))){ + trtp_rtcp_packet_init(TRTP_RTCP_PACKET(bye), TRTP_RTCP_HEADER_VERSION_DEFAULT, 0, 0, trtp_rtcp_packet_type_bye, TRTP_RTCP_HEADER_SIZE); + } + return bye; +} + +trtp_rtcp_report_bye_t* trtp_rtcp_report_bye_create(struct trtp_rtcp_header_s* header) +{ + trtp_rtcp_report_bye_t* bye; + if((bye = (trtp_rtcp_report_bye_t*)tsk_object_new(trtp_rtcp_report_bye_def_t))){ + TRTP_RTCP_PACKET(bye)->header = tsk_object_ref(header); + } + return bye; +} + +trtp_rtcp_report_bye_t* trtp_rtcp_report_bye_create_2(uint32_t ssrc) +{ + uint32_t* ssrc_list = tsk_malloc(sizeof(uint32_t)); + if(ssrc_list){ + trtp_rtcp_report_bye_t* bye; + if((bye = (trtp_rtcp_report_bye_t*)tsk_object_new(trtp_rtcp_report_bye_def_t))){ + trtp_rtcp_packet_init(TRTP_RTCP_PACKET(bye), TRTP_RTCP_HEADER_VERSION_DEFAULT, 0, 0, trtp_rtcp_packet_type_bye, (TRTP_RTCP_HEADER_SIZE + 4)); + TRTP_RTCP_PACKET(bye)->header->rc = 1; + *ssrc_list = ssrc, bye->ssrc_list = ssrc_list, ssrc_list = tsk_null; + } + TSK_FREE(ssrc_list); + return bye; + } + return tsk_null; +} + +trtp_rtcp_report_bye_t* trtp_rtcp_report_bye_deserialize(const void* data, tsk_size_t _size) +{ + trtp_rtcp_report_bye_t* bye = tsk_null; + trtp_rtcp_header_t* header = tsk_null; + const uint8_t* pdata = (const uint8_t*)data; + int32_t size = (int32_t)_size; + + if(!data || size < TRTP_RTCP_PACKET_BYE_MIN_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + if(!(header = trtp_rtcp_header_deserialize(pdata, size))){ + TSK_DEBUG_ERROR("Failed to deserialize the header"); + goto bail; + } + if(header->length_in_bytes < TRTP_RTCP_PACKET_BYE_MIN_SIZE){ + TSK_DEBUG_ERROR("Too short"); + goto bail; + } + + if(!(bye = trtp_rtcp_report_bye_create(header))){ + TSK_DEBUG_ERROR("Failed to create object"); + goto bail; + } + + pdata += (TRTP_RTCP_HEADER_SIZE); + size -= (TRTP_RTCP_HEADER_SIZE); + + // SSRCs + if(header->rc > 0){ + tsk_size_t i; + if((int32_t)(header->rc * sizeof(uint32_t)) > size){ + TSK_DEBUG_ERROR("Too short"); + goto bail; + } + if(!(bye->ssrc_list = tsk_calloc(header->rc, sizeof(uint32_t)))){ + goto bail; + } + for(i = 0; i < header->rc; ++i){ + bye->ssrc_list[i] = (uint32_t)tnet_ntohl_2(&pdata[0]); + pdata += sizeof(uint32_t); + size -= sizeof(uint32_t); + } + } + + // Other Packets + while(size > TRTP_RTCP_HEADER_SIZE){ + trtp_rtcp_packet_t* packet; + + if((packet = trtp_rtcp_packet_deserialize(pdata, size))){ + if((size -= packet->header->length_in_bytes) > 0){ + pdata += packet->header->length_in_bytes; + } + tsk_list_push_back_data(bye->packets, (void**)&packet); + continue; + } + break; + } + +bail: + TSK_OBJECT_SAFE_FREE(header); + return bye; +} + +int trtp_rtcp_report_bye_serialize_to(const trtp_rtcp_report_bye_t* self, void* data, tsk_size_t size) +{ + int ret; + tsk_size_t i; + uint8_t* pdata = (uint8_t*)data; + const tsk_list_item_t* item; + + if(!self || !data || size < trtp_rtcp_report_bye_get_size(self)){ + return -1; + } + + if((ret = trtp_rtcp_header_serialize_to(TRTP_RTCP_PACKET(self)->header, pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize the header"); + return ret; + } + + pdata += (TRTP_RTCP_HEADER_SIZE); + size -= (TRTP_RTCP_HEADER_SIZE); + + for(i = 0; i < TRTP_RTCP_PACKET(self)->header->rc; ++i){ + pdata[0] = self->ssrc_list[i] >> 24; + pdata[1] = (self->ssrc_list[i] >> 16) & 0xFF; + pdata[2] = (self->ssrc_list[i] >> 8) & 0xFF; + pdata[3] = (self->ssrc_list[i] & 0xFF); + pdata += 4; + size -= 4; + } + + tsk_list_foreach(item, self->packets){ + if(!item->data){ + continue; + } + if((ret = trtp_rtcp_packet_serialize_to(TRTP_RTCP_PACKET(item->data), pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize packet"); + goto bail; + } + pdata += TRTP_RTCP_PACKET(item->data)->header->length_in_bytes; + size -= TRTP_RTCP_PACKET(item->data)->header->length_in_bytes; + } + +bail: + return ret; +} + +tsk_size_t trtp_rtcp_report_bye_get_size(const trtp_rtcp_report_bye_t* self) +{ + tsk_size_t size; + const tsk_list_item_t* item; + + if(!self || !TRTP_RTCP_PACKET(self)->header){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + + size = TRTP_RTCP_PACKET(self)->header->length_in_bytes; + tsk_list_foreach(item, self->packets){ + if(item->data && TRTP_RTCP_PACKET(item->data)->header){ + size += TRTP_RTCP_PACKET(item->data)->header->length_in_bytes; + } + } + + return size; +} diff --git a/tinyRTP/src/rtcp/trtp_rtcp_report_fb.c b/tinyRTP/src/rtcp/trtp_rtcp_report_fb.c new file mode 100644 index 0000000..bbc80b1 --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_report_fb.c @@ -0,0 +1,651 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WArtpfbANTY; without even the implied wartpfbanty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinyrtp/rtcp/trtp_rtcp_report_fb.h" +#include "tinyrtp/rtcp/trtp_rtcp_header.h" + +#include "tnet_endianness.h" + +#include "tsk_string.h" +#include "tsk_memory.h" +#include "tsk_debug.h" + +#include <stdlib.h> +#include <string.h> + +#define TRTP_RTCP_PACKET_FB_MIN_SIZE (TRTP_RTCP_HEADER_SIZE + 4 + 4) + +static int _trtp_rtcp_report_fb_deserialize(const void* data, tsk_size_t _size, trtp_rtcp_header_t** header, uint32_t* ssrc_sender, uint32_t* ssrc_media_src) +{ + const uint8_t* pdata = data; + if(!data || !header || _size < TRTP_RTCP_PACKET_FB_MIN_SIZE || (_size & 0x03)){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(!(*header = trtp_rtcp_header_deserialize(pdata, _size))){ + TSK_DEBUG_ERROR("Failed to deserialize the header"); + return -3; + } + if((*header)->length_in_bytes < TRTP_RTCP_PACKET_FB_MIN_SIZE){ + TSK_DEBUG_ERROR("Too short"); + return -4; + } + else if((*header)->length_in_bytes > _size){ + TSK_DEBUG_ERROR("Too long"); + return -5; + } + + *ssrc_sender = (uint32_t)tnet_ntohl_2(&pdata[4]); + *ssrc_media_src = (uint32_t)tnet_ntohl_2(&pdata[8]); + return 0; +} + + +static int _trtp_rtcp_report_fb_serialize_to(const trtp_rtcp_report_fb_t* self, void* data, tsk_size_t size) +{ + int ret; + uint8_t* pdata = (uint8_t*)data; + + if(!self || !data || size < TRTP_RTCP_PACKET_FB_MIN_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if((ret = trtp_rtcp_header_serialize_to(TRTP_RTCP_PACKET(self)->header, pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize the header"); + return ret; + } + + pdata[TRTP_RTCP_HEADER_SIZE] = self->ssrc_sender >> 24; + pdata[TRTP_RTCP_HEADER_SIZE + 1] = (self->ssrc_sender >> 16) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 2] = (self->ssrc_sender >> 8) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 3] = (self->ssrc_sender & 0xFF); + pdata[TRTP_RTCP_HEADER_SIZE + 4] = self->ssrc_media >> 24; + pdata[TRTP_RTCP_HEADER_SIZE + 5] = (self->ssrc_media >> 16) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 6] = (self->ssrc_media >> 8) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 7] = (self->ssrc_media & 0xFF); + + return 0; +} + + + +static tsk_object_t* trtp_rtcp_report_rtpfb_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_report_rtpfb_t *rtpfb = self; + if(rtpfb){ + + } + return self; +} +static tsk_object_t* trtp_rtcp_report_rtpfb_dtor(tsk_object_t * self) +{ + trtp_rtcp_report_rtpfb_t *rtpfb = self; + if(rtpfb){ + // deinit self + switch(rtpfb->fci_type){ + case trtp_rtcp_rtpfb_fci_type_nack: + { + TSK_FREE(rtpfb->nack.pid); + TSK_FREE(rtpfb->nack.blp); + break; + } + case trtp_rtcp_rtpfb_fci_type_tmmbn: + { + TSK_FREE(rtpfb->tmmbn.ssrc); + TSK_FREE(rtpfb->tmmbn.MxTBR_Exp); + TSK_FREE(rtpfb->tmmbn.MxTBR_Mantissa); + TSK_FREE(rtpfb->tmmbn.MeasuredOverhead); + break; + } + } + // deinit base + trtp_rtcp_packet_deinit(TRTP_RTCP_PACKET(rtpfb)); + } + + return self; +} +static const tsk_object_def_t trtp_rtcp_report_rtpfb_def_s = +{ + sizeof(trtp_rtcp_report_rtpfb_t), + trtp_rtcp_report_rtpfb_ctor, + trtp_rtcp_report_rtpfb_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_report_rtpfb_def_t = &trtp_rtcp_report_rtpfb_def_s; + +trtp_rtcp_report_rtpfb_t* trtp_rtcp_report_rtpfb_create_null() +{ + trtp_rtcp_report_rtpfb_t* rtpfb; + if((rtpfb = (trtp_rtcp_report_rtpfb_t*)tsk_object_new(trtp_rtcp_report_rtpfb_def_t))){ + trtp_rtcp_packet_init(TRTP_RTCP_PACKET(rtpfb), TRTP_RTCP_HEADER_VERSION_DEFAULT, 0, 0, trtp_rtcp_packet_type_rtpfb, TRTP_RTCP_PACKET_FB_MIN_SIZE); + } + return rtpfb; +} + +trtp_rtcp_report_rtpfb_t* trtp_rtcp_report_rtpfb_create(trtp_rtcp_header_t* header) +{ + trtp_rtcp_report_rtpfb_t* rtpfb; + if((rtpfb = (trtp_rtcp_report_rtpfb_t*)tsk_object_new(trtp_rtcp_report_rtpfb_def_t))){ + TRTP_RTCP_PACKET(rtpfb)->header = tsk_object_ref(header); + } + return rtpfb; +} + +trtp_rtcp_report_rtpfb_t* trtp_rtcp_report_rtpfb_create_2(trtp_rtcp_rtpfb_fci_type_t fci_type, uint32_t ssrc_sender, uint32_t ssrc_media_src) +{ + trtp_rtcp_report_rtpfb_t* rtpfb; + if((rtpfb = trtp_rtcp_report_rtpfb_create_null())){ + rtpfb->fci_type = TRTP_RTCP_PACKET(rtpfb)->header->rc = fci_type; + TRTP_RTCP_REPORT_FB(rtpfb)->ssrc_sender = ssrc_sender; + TRTP_RTCP_REPORT_FB(rtpfb)->ssrc_media = ssrc_media_src; + } + return rtpfb; +} + +// seq_nums[n] must be in [seq_nums[0], seq_nums[0] + 16] and > seq_nums[n - 1] +trtp_rtcp_report_rtpfb_t* trtp_rtcp_report_rtpfb_create_nack(uint32_t ssrc_sender, uint32_t ssrc_media_src, const uint16_t* seq_nums, tsk_size_t count) +{ + trtp_rtcp_report_rtpfb_t* rtpfb; + if(!seq_nums || !count){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + if((rtpfb = trtp_rtcp_report_rtpfb_create_2(trtp_rtcp_rtpfb_fci_type_nack, ssrc_sender, ssrc_media_src))){ + tsk_size_t i, j; + rtpfb->nack.count = 1; // max = 16 + rtpfb->nack.blp = tsk_malloc(sizeof(uint16_t)); + rtpfb->nack.pid = tsk_malloc(sizeof(uint16_t)); + if(!rtpfb->nack.blp || !rtpfb->nack.pid){ + TSK_OBJECT_SAFE_FREE(rtpfb); + return tsk_null; + } + rtpfb->nack.pid[0] = seq_nums[0]; + rtpfb->nack.blp[0] = 0; + for(i = 1; i <= 16 && i < count; ++i){ + j = seq_nums[i] - rtpfb->nack.pid[0]; + rtpfb->nack.blp[0] |= (1 << (j - 1)); + } + + TRTP_RTCP_PACKET(rtpfb)->header->length_in_bytes += (uint32_t)(rtpfb->nack.count << 2); + TRTP_RTCP_PACKET(rtpfb)->header->length_in_words_minus1 = ((TRTP_RTCP_PACKET(rtpfb)->header->length_in_bytes >> 2) - 1); + } + return rtpfb; +} + + +trtp_rtcp_report_rtpfb_t* trtp_rtcp_report_rtpfb_deserialize(const void* data, tsk_size_t _size) +{ + trtp_rtcp_report_rtpfb_t* rtpfb = tsk_null; + trtp_rtcp_header_t* header = tsk_null; + uint32_t ssrc_sender, ssrc_media_src; + + if(_trtp_rtcp_report_fb_deserialize(data, _size, &header, &ssrc_sender, &ssrc_media_src) == 0){ + if((rtpfb = trtp_rtcp_report_rtpfb_create(header))){ + const uint8_t* pdata = ((const uint8_t*)data) + TRTP_RTCP_PACKET_FB_MIN_SIZE; + tsk_size_t size = (header->length_in_bytes - TRTP_RTCP_PACKET_FB_MIN_SIZE), i; + + TRTP_RTCP_REPORT_FB(rtpfb)->ssrc_sender = ssrc_sender; + TRTP_RTCP_REPORT_FB(rtpfb)->ssrc_media = ssrc_media_src; + + switch(rtpfb->fci_type = (trtp_rtcp_rtpfb_fci_type_t)header->rc){ + case trtp_rtcp_rtpfb_fci_type_nack: + { + if((rtpfb->nack.count = (size >> 2)) > 0){ + rtpfb->nack.pid = tsk_realloc(rtpfb->nack.pid, (rtpfb->nack.count * sizeof(uint16_t))); + rtpfb->nack.blp = tsk_realloc(rtpfb->nack.blp, (rtpfb->nack.count * sizeof(uint16_t))); + for(i = 0; i < rtpfb->nack.count; ++i){ + if(rtpfb->nack.pid) rtpfb->nack.pid[i] = tnet_ntohs_2(&pdata[0]); + if(rtpfb->nack.blp) rtpfb->nack.blp[i] = tnet_ntohs_2(&pdata[2]); + pdata += 4; + } + } + break; + } + case trtp_rtcp_rtpfb_fci_type_tmmbn: + { + TSK_DEBUG_INFO("TMMBN"); + if((rtpfb->tmmbn.count = (size >> 3)) > 0){ + uint32_t u32; + rtpfb->tmmbn.ssrc = tsk_realloc(rtpfb->tmmbn.ssrc, (rtpfb->tmmbn.count * sizeof(uint32_t))); + rtpfb->tmmbn.MxTBR_Exp = tsk_realloc(rtpfb->tmmbn.MxTBR_Exp, (rtpfb->tmmbn.count * sizeof(uint16_t))); + rtpfb->tmmbn.MxTBR_Mantissa = tsk_realloc(rtpfb->tmmbn.MxTBR_Mantissa, (rtpfb->tmmbn.count * sizeof(uint32_t))); + rtpfb->tmmbn.MeasuredOverhead = tsk_realloc(rtpfb->tmmbn.MeasuredOverhead, (rtpfb->tmmbn.count * sizeof(uint16_t))); + for(i = 0; i < rtpfb->tmmbn.count; ++i){ + if(rtpfb->tmmbn.ssrc) rtpfb->tmmbn.ssrc[i] = (uint32_t)tnet_ntohl_2(&pdata[0]); + u32 = (uint32_t)tnet_ntohl_2(&pdata[4]); + if(rtpfb->tmmbn.MxTBR_Exp) rtpfb->tmmbn.MxTBR_Exp[i] = (u32 >> 26); + if(rtpfb->tmmbn.MxTBR_Mantissa) rtpfb->tmmbn.MxTBR_Mantissa[i] = ((u32 >> 9) & 0x1FFFF); + if(rtpfb->tmmbn.MeasuredOverhead) rtpfb->tmmbn.MeasuredOverhead[i] = (u32 & 0x1FF); + pdata += 8; + } + } + break; + } + + default: + { + TSK_DEBUG_ERROR("Unsupported Feedback message type %d", (int)rtpfb->fci_type); + break; + } + } + } + } + + TSK_OBJECT_SAFE_FREE(header); + return rtpfb; +} + +int trtp_rtcp_report_rtpfb_serialize_to(const trtp_rtcp_report_rtpfb_t* self, void* data, tsk_size_t size) +{ + int ret; + uint8_t* pdata = (uint8_t*)data; + + if(!self || !data || size < trtp_rtcp_report_rtpfb_get_size(self)){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if((ret = _trtp_rtcp_report_fb_serialize_to(TRTP_RTCP_REPORT_FB(self), pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize FB message"); + return ret; + } + pdata += TRTP_RTCP_PACKET_FB_MIN_SIZE; + size -= TRTP_RTCP_PACKET_FB_MIN_SIZE; + + switch(self->fci_type){ + case trtp_rtcp_rtpfb_fci_type_nack: + { + tsk_size_t i; + for(i = 0; i < self->nack.count; ++i){ + pdata[0] = self->nack.pid[i] >> 8; + pdata[1] = (self->nack.pid[i] & 0xFF); + pdata[2] = self->nack.blp[i] >> 8; + pdata[3] = (self->nack.blp[i] & 0xFF); + pdata += 4; + } + break; + } + default: + { + TSK_DEBUG_ERROR("Not implemented"); + return -2; + } + } + return 0; +} + +tsk_size_t trtp_rtcp_report_rtpfb_get_size(const trtp_rtcp_report_rtpfb_t* self) +{ + if(!self || !TRTP_RTCP_PACKET(self)->header){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + return TRTP_RTCP_PACKET(self)->header->length_in_bytes; +} + + + + + + + + + +static tsk_object_t* trtp_rtcp_report_psfb_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_report_psfb_t *psfb = self; + if(psfb){ + + } + return self; +} +static tsk_object_t* trtp_rtcp_report_psfb_dtor(tsk_object_t * self) +{ + trtp_rtcp_report_psfb_t *psfb = self; + if(psfb){ + // deinit self + switch(psfb->fci_type){ + case trtp_rtcp_psfb_fci_type_pli: + break; + case trtp_rtcp_psfb_fci_type_sli: + TSK_FREE(psfb->sli.first); + TSK_FREE(psfb->sli.number); + TSK_FREE(psfb->sli.pic_id); + break; + case trtp_rtcp_psfb_fci_type_rpsi: + TSK_FREE(psfb->rpsi.bytes); + break; + case trtp_rtcp_psfb_fci_type_fir: + TSK_FREE(psfb->fir.ssrc); + TSK_FREE(psfb->fir.seq_num); + break; + case trtp_rtcp_psfb_fci_type_afb: + switch(psfb->afb.type){ + case trtp_rtcp_psfb_afb_type_none: + TSK_FREE(psfb->afb.none.bytes); + break; + case trtp_rtcp_psfb_afb_type_remb: + TSK_FREE(psfb->afb.remb.ssrc_feedbacks); + break; + } + break; + } + // deinit base + trtp_rtcp_packet_deinit(TRTP_RTCP_PACKET(psfb)); + } + + return self; +} +static const tsk_object_def_t trtp_rtcp_report_psfb_def_s = +{ + sizeof(trtp_rtcp_report_psfb_t), + trtp_rtcp_report_psfb_ctor, + trtp_rtcp_report_psfb_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_report_psfb_def_t = &trtp_rtcp_report_psfb_def_s; + + +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create_null() +{ + trtp_rtcp_report_psfb_t* psfb; + if((psfb = (trtp_rtcp_report_psfb_t*)tsk_object_new(trtp_rtcp_report_psfb_def_t))){ + trtp_rtcp_packet_init(TRTP_RTCP_PACKET(psfb), TRTP_RTCP_HEADER_VERSION_DEFAULT, 0, 0, trtp_rtcp_packet_type_psfb, TRTP_RTCP_PACKET_FB_MIN_SIZE); + } + return psfb; +} + +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create(trtp_rtcp_header_t* header) +{ + trtp_rtcp_report_psfb_t* psfb; + if((psfb = (trtp_rtcp_report_psfb_t*)tsk_object_new(trtp_rtcp_report_psfb_def_t))){ + TRTP_RTCP_PACKET(psfb)->header = tsk_object_ref(header); + } + return psfb; +} + +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create_2(trtp_rtcp_psfb_fci_type_t fci_type, uint32_t ssrc_sender, uint32_t ssrc_media_src) +{ + trtp_rtcp_report_psfb_t* psfb; + if((psfb = trtp_rtcp_report_psfb_create_null())){ + TRTP_RTCP_PACKET(psfb)->header->rc = psfb->fci_type = fci_type; + TRTP_RTCP_REPORT_FB(psfb)->ssrc_sender = ssrc_sender; + TRTP_RTCP_REPORT_FB(psfb)->ssrc_media = ssrc_media_src; + } + return psfb; +} + +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create_pli(uint32_t ssrc_sender, uint32_t ssrc_media_src) +{ + return trtp_rtcp_report_psfb_create_2(trtp_rtcp_psfb_fci_type_pli, ssrc_sender, ssrc_media_src); +} + +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create_fir(uint8_t seq_num, uint32_t ssrc_sender, uint32_t ssrc_media_src) +{ + trtp_rtcp_report_psfb_t* psfb; + if((psfb = trtp_rtcp_report_psfb_create_2(trtp_rtcp_psfb_fci_type_fir, ssrc_sender, ssrc_media_src))){ + psfb->fir.ssrc = tsk_malloc(sizeof(uint32_t)); + psfb->fir.seq_num = tsk_malloc(sizeof(uint8_t)); + if(!psfb->fir.ssrc || !psfb->fir.seq_num){ + TSK_OBJECT_SAFE_FREE(psfb); + return tsk_null; + } + psfb->fir.count = 1; + psfb->fir.seq_num[0] = seq_num; + psfb->fir.ssrc[0] = ssrc_media_src; + TRTP_RTCP_PACKET(psfb)->header->length_in_bytes += (uint32_t)(psfb->fir.count << 3); + TRTP_RTCP_PACKET(psfb)->header->length_in_words_minus1 = ((TRTP_RTCP_PACKET(psfb)->header->length_in_bytes >> 2) - 1); + } + return psfb; +} + +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_create_afb_remb(uint32_t ssrc_sender, const uint32_t* ssrc_media_src_list, uint32_t ssrc_media_src_list_count, uint32_t bitrate/*in bps*/) +{ + trtp_rtcp_report_psfb_t* psfb; + // draft-alvestrand-rmcat-remb-02 2.2: SSRC media source always equal to zero + if((psfb = trtp_rtcp_report_psfb_create_2(trtp_rtcp_psfb_fci_type_afb, ssrc_sender, 0))){ + static const uint32_t __max_mantissa = 131072; + psfb->afb.type = trtp_rtcp_psfb_afb_type_remb; + psfb->afb.remb.exp = 0; + if(bitrate <= __max_mantissa){ + psfb->afb.remb.mantissa = bitrate; + } + else{ + while(bitrate >= (__max_mantissa << psfb->afb.remb.exp) && psfb->afb.remb.exp < 63) ++psfb->afb.remb.exp; + psfb->afb.remb.mantissa = (bitrate >> psfb->afb.remb.exp); + } + if(ssrc_media_src_list && ssrc_media_src_list_count > 0 && (psfb->afb.remb.ssrc_feedbacks = (uint32_t*)tsk_malloc(ssrc_media_src_list_count << 2))){ + uint32_t i; + psfb->afb.remb.num_ssrc = ssrc_media_src_list_count; + for(i = 0; i < ssrc_media_src_list_count; ++i){ + psfb->afb.remb.ssrc_feedbacks[i] = ssrc_media_src_list[i]; + } + } + TRTP_RTCP_PACKET(psfb)->header->length_in_bytes += 8; /*'R' 'E' 'M' 'B', Num SSRC, BR Exp, BR Mantissa */ + TRTP_RTCP_PACKET(psfb)->header->length_in_bytes += (psfb->afb.remb.num_ssrc << 2); + TRTP_RTCP_PACKET(psfb)->header->length_in_words_minus1 = ((TRTP_RTCP_PACKET(psfb)->header->length_in_bytes >> 2) - 1); + } + return psfb; +} + +trtp_rtcp_report_psfb_t* trtp_rtcp_report_psfb_deserialize(const void* data, tsk_size_t _size) +{ + trtp_rtcp_report_psfb_t* psfb = tsk_null; + trtp_rtcp_header_t* header = tsk_null; + uint32_t ssrc_sender, ssrc_media_src; + + if(_trtp_rtcp_report_fb_deserialize(data, _size, &header, &ssrc_sender, &ssrc_media_src) == 0){ + if((psfb = trtp_rtcp_report_psfb_create(header))){ + const uint8_t* pdata = ((const uint8_t*)data) + TRTP_RTCP_PACKET_FB_MIN_SIZE; + tsk_size_t size = (header->length_in_bytes - TRTP_RTCP_PACKET_FB_MIN_SIZE); + + TRTP_RTCP_REPORT_FB(psfb)->ssrc_sender = ssrc_sender; + TRTP_RTCP_REPORT_FB(psfb)->ssrc_media = ssrc_media_src; + + switch((psfb->fci_type = header->rc)/* FMT for RTCP-FB messages */){ + case trtp_rtcp_psfb_fci_type_pli: + { + // No FCI in PLI + // TSK_DEBUG_INFO("PLI"); + break; + } + case trtp_rtcp_psfb_fci_type_sli: + { + tsk_size_t sli_count = (size >> 2), i; + uint32_t u32; + if(sli_count == 0){ + TSK_DEBUG_ERROR("Too short"); + goto bail; + } + psfb->sli.first = tsk_realloc(psfb->sli.first, (sli_count * sizeof(uint16_t))); + psfb->sli.number = tsk_realloc(psfb->sli.number, (sli_count * sizeof(uint16_t))); + psfb->sli.pic_id = tsk_realloc(psfb->sli.pic_id, (sli_count * sizeof(uint16_t))); + for(i = 0; i < sli_count; ++i){ + u32 = (uint32_t)tnet_ntohl_2(&pdata[i >> 2]); + if(psfb->sli.first) psfb->sli.first[i] = (u32 >> 19); + if(psfb->sli.number) psfb->sli.number[i] = (u32 >> 6) & 0x1FFF; + if(psfb->sli.pic_id) psfb->sli.pic_id[i] = u32 & 0x3F; + } + + break; + } + case trtp_rtcp_psfb_fci_type_rpsi: + { + uint16_t u16; + if(size < 2){ + TSK_DEBUG_ERROR("Too short"); + goto bail; + } + u16 = tnet_ntohs_2(&pdata[0]); + psfb->rpsi.pb = (u16 >> 8); + psfb->rpsi.pt = (u16 & 0x7F); + if((psfb->rpsi.bytes = tsk_calloc((size - 2), sizeof(uint8_t)))){ + memcpy(psfb->rpsi.bytes, &pdata[2], (size - 2)); + } + break; + } + case trtp_rtcp_psfb_fci_type_fir: + { + tsk_size_t fir_count = (size >> 3), i; + if(fir_count == 0){ + TSK_DEBUG_ERROR("Too short"); + goto bail; + } + psfb->fir.count = fir_count; + psfb->fir.ssrc = tsk_realloc(psfb->fir.seq_num, (fir_count * sizeof(uint32_t))); + psfb->fir.seq_num = tsk_realloc(psfb->fir.seq_num, (fir_count * sizeof(uint8_t))); + for(i = 0; i < fir_count; ++i){ + if(psfb->fir.ssrc) psfb->fir.ssrc[i] = (uint32_t)tnet_ntohl_2(&pdata[0]); + if(psfb->fir.seq_num) psfb->fir.seq_num[i] = pdata[4]; + pdata+=8; + } + break; + } + case trtp_rtcp_psfb_fci_type_afb: + { + if(size > 0){ + psfb->afb.type = trtp_rtcp_psfb_afb_type_none; + // REMB (http://tools.ietf.org/html/draft-alvestrand-rmcat-remb-02) ? + if(size > 4 && tsk_strniequals(pdata, "REMB", 4)){ + uint32_t _u32; + if(size < 8){ // REMB, Num SSRC, BR Exp, BR Mantissa + TSK_DEBUG_ERROR("Too short"); + goto bail; + } + psfb->afb.type = trtp_rtcp_psfb_afb_type_remb; + _u32 = (uint32_t)tnet_ntohl_2(&pdata[4]); + psfb->afb.remb.num_ssrc = ((_u32 >> 24) & 0xFF); + if((psfb->afb.remb.num_ssrc << 2) != (size - 8)){ + TSK_DEBUG_ERROR("Invalid size"); + psfb->afb.remb.num_ssrc = 0; + goto bail; + } + psfb->afb.remb.exp = ((_u32 >> 18) & 0x3F); + psfb->afb.remb.mantissa = (_u32 & 0x3FFFF); + if((psfb->afb.remb.ssrc_feedbacks = tsk_malloc(psfb->afb.remb.num_ssrc << 2))){ + for(_u32 = 0; _u32 < psfb->afb.remb.num_ssrc; ++_u32){ + psfb->afb.remb.ssrc_feedbacks[_u32] = (uint32_t)tnet_ntohl_2(&pdata[8 + (_u32 << 2)]); + } + } + } + else{ + if((psfb->afb.none.bytes = tsk_calloc(size, sizeof(uint8_t)))){ + memcpy(psfb->afb.none.bytes, &pdata[0], size); + } + } + } + break; + } + default: + { + TSK_DEBUG_ERROR("%d not a valid FCI", psfb->fci_type); + goto bail; + } + } + } + } + +bail: + TSK_OBJECT_SAFE_FREE(header); + return psfb; +} + +int trtp_rtcp_report_psfb_serialize_to(const trtp_rtcp_report_psfb_t* self, void* data, tsk_size_t size) +{ + int ret; + uint8_t* pdata = (uint8_t*)data; + + if(!self || !data || size < trtp_rtcp_report_psfb_get_size(self)){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if((ret = _trtp_rtcp_report_fb_serialize_to(TRTP_RTCP_REPORT_FB(self), pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize FB message"); + return ret; + } + pdata += TRTP_RTCP_PACKET_FB_MIN_SIZE; + size -= TRTP_RTCP_PACKET_FB_MIN_SIZE; + + switch(self->fci_type){ + case trtp_rtcp_psfb_fci_type_pli: + { + // No FCI in PLI + break; + } + case trtp_rtcp_psfb_fci_type_fir: + { + tsk_size_t i; + for(i = 0; i < self->fir.count; ++i){ + pdata[0] = self->fir.ssrc[i] >> 24; + pdata[1] = (self->fir.ssrc[i] >> 16) & 0xFF; + pdata[2] = (self->fir.ssrc[i] >> 8) & 0xFF; + pdata[3] = (self->fir.ssrc[i] & 0xFF); + pdata[4] = self->fir.seq_num[i]; + pdata += 8; // SSRC (4), Seq nr(1), Reserved(3) + } + break; + } + case trtp_rtcp_psfb_fci_type_afb: + { + if(self->afb.type == trtp_rtcp_psfb_afb_type_remb){ + tsk_size_t i; + // 'R' 'E' 'M' 'B' + pdata[0] = 'R', pdata[1] = 'E', pdata[2] = 'M', pdata[3] = 'B'; + // | Num SSRC | BR Exp | BR Mantissa + pdata[4] = self->afb.remb.num_ssrc; // 8bits + pdata[5] = (self->afb.remb.exp << 2) & 0xFC;// 6bits + // 18bits + pdata[5] |= (self->afb.remb.mantissa >> 16) & 0x3; + pdata[6] = (self->afb.remb.mantissa >> 8) & 0xFF; + pdata[7] = (self->afb.remb.mantissa & 0xFF); + if(self->afb.remb.ssrc_feedbacks){ + for(i = 0; i < self->afb.remb.num_ssrc; ++i){ + pdata[8 + (i<<2)] = self->afb.remb.ssrc_feedbacks[i] >> 24; + pdata[8 + (i<<2) + 1] = (self->afb.remb.ssrc_feedbacks[i] >> 16) & 0xFF; + pdata[8 + (i<<2) + 2] = (self->afb.remb.ssrc_feedbacks[i] >> 8) & 0xFF; + pdata[8 + (i<<2) + 3] = (self->afb.remb.ssrc_feedbacks[i] & 0xFF); + } + } + } + else{ + TSK_DEBUG_ERROR("Not implemented yet"); + return -1; + } + break; + } + default: + { + TSK_DEBUG_ERROR("Not implemented yet"); + return -1; + } + } + + return ret; +} + +tsk_size_t trtp_rtcp_report_psfb_get_size(const trtp_rtcp_report_psfb_t* self) +{ + if(!self || !TRTP_RTCP_PACKET(self)->header){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + return TRTP_RTCP_PACKET(self)->header->length_in_bytes; +}
\ No newline at end of file diff --git a/tinyRTP/src/rtcp/trtp_rtcp_report_rr.c b/tinyRTP/src/rtcp/trtp_rtcp_report_rr.c new file mode 100644 index 0000000..2a38a4c --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_report_rr.c @@ -0,0 +1,232 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinyrtp/rtcp/trtp_rtcp_report_rr.h" +#include "tinyrtp/rtcp/trtp_rtcp_header.h" + +#include "tnet_endianness.h" + +#include "tsk_debug.h" + +#define TRTP_RTCP_PACKET_RR_MIN_SIZE (TRTP_RTCP_HEADER_SIZE + 4) + +static tsk_object_t* trtp_rtcp_report_rr_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_report_rr_t *rr = self; + if(rr){ + rr->packets = tsk_list_create(); + rr->blocks = tsk_list_create(); + } + return self; +} +static tsk_object_t* trtp_rtcp_report_rr_dtor(tsk_object_t * self) +{ + trtp_rtcp_report_rr_t *rr = self; + if(rr){ + // deinit self + TSK_OBJECT_SAFE_FREE(rr->packets); + TSK_OBJECT_SAFE_FREE(rr->blocks); + // deinit base + trtp_rtcp_packet_deinit(TRTP_RTCP_PACKET(rr)); + } + + return self; +} +static const tsk_object_def_t trtp_rtcp_report_rr_def_s = +{ + sizeof(trtp_rtcp_report_rr_t), + trtp_rtcp_report_rr_ctor, + trtp_rtcp_report_rr_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_report_rr_def_t = &trtp_rtcp_report_rr_def_s; + + +trtp_rtcp_report_rr_t* trtp_rtcp_report_rr_create_null() +{ + trtp_rtcp_report_rr_t* rr; + if((rr = (trtp_rtcp_report_rr_t*)tsk_object_new(trtp_rtcp_report_rr_def_t))){ + trtp_rtcp_packet_init(TRTP_RTCP_PACKET(rr), TRTP_RTCP_HEADER_VERSION_DEFAULT, 0, 0, trtp_rtcp_packet_type_rr, (TRTP_RTCP_HEADER_SIZE + 4)); + } + return rr; +} + +trtp_rtcp_report_rr_t* trtp_rtcp_report_rr_create(trtp_rtcp_header_t* header) +{ + trtp_rtcp_report_rr_t* rr; + if((rr = (trtp_rtcp_report_rr_t*)tsk_object_new(trtp_rtcp_report_rr_def_t))){ + TRTP_RTCP_PACKET(rr)->header = tsk_object_ref(header); + } + return rr; +} + +trtp_rtcp_report_rr_t* trtp_rtcp_report_rr_create_2(uint32_t ssrc) +{ + trtp_rtcp_report_rr_t* rr; + if((rr = trtp_rtcp_report_rr_create_null())){ + rr->ssrc = ssrc; + } + return rr; +} + +trtp_rtcp_report_rr_t* trtp_rtcp_report_rr_deserialize(const void* data, tsk_size_t _size) +{ + trtp_rtcp_report_rr_t* rr = tsk_null; + trtp_rtcp_header_t* header = tsk_null; + const uint8_t* pdata = (const uint8_t*)data; + int32_t size = (int32_t)_size; + + if(!data || size < TRTP_RTCP_PACKET_RR_MIN_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + if(!(header = trtp_rtcp_header_deserialize(pdata, size))){ + TSK_DEBUG_ERROR("Failed to deserialize the header"); + goto bail; + } + if(header->length_in_bytes < TRTP_RTCP_PACKET_RR_MIN_SIZE){ + TSK_DEBUG_ERROR("Too short"); + goto bail; + } + + if(!(rr = trtp_rtcp_report_rr_create(header))){ + TSK_DEBUG_ERROR("Failed to create object"); + goto bail; + } + + rr->ssrc = (uint32_t)tnet_ntohl_2(&pdata[4]); + + pdata += (TRTP_RTCP_HEADER_SIZE + 4); + size -= (TRTP_RTCP_HEADER_SIZE + 4); + + // Blocks + if(header->rc > 0){ + tsk_size_t i = 0; + trtp_rtcp_rblock_t* rblock; + + while(i++ < header->rc && size >= TRTP_RTCP_RBLOCK_SIZE){ + if((rblock = trtp_rtcp_rblock_deserialize(pdata, size))){ + tsk_list_push_back_data(rr->blocks, (void**)&rblock); + } + pdata += TRTP_RTCP_RBLOCK_SIZE; + size -= TRTP_RTCP_RBLOCK_SIZE; + } + } + + // Other Packets + while(size > TRTP_RTCP_HEADER_SIZE){ + trtp_rtcp_packet_t* packet; + + if((packet = trtp_rtcp_packet_deserialize(pdata, size))){ + if((size -= packet->header->length_in_bytes) > 0){ + pdata += packet->header->length_in_bytes; + } + tsk_list_push_back_data(rr->packets, (void**)&packet); + continue; + } + break; + } + +bail: + TSK_OBJECT_SAFE_FREE(header); + return rr; +} + +int trtp_rtcp_report_rr_serialize_to(const trtp_rtcp_report_rr_t* self, void* data, tsk_size_t size) +{ + int ret; + const tsk_list_item_t* item; + uint8_t* pdata = (uint8_t*)data; + + if(!self || !data || size < trtp_rtcp_report_rr_get_size(self)){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if((ret = trtp_rtcp_header_serialize_to(TRTP_RTCP_PACKET(self)->header, pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize the header"); + return ret; + } + + pdata[TRTP_RTCP_HEADER_SIZE] = self->ssrc >> 24; + pdata[TRTP_RTCP_HEADER_SIZE + 1] = (self->ssrc >> 16) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 2] = (self->ssrc >> 8) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 3] = (self->ssrc & 0xFF); + + pdata += (TRTP_RTCP_HEADER_SIZE + 4); + size -= (TRTP_RTCP_HEADER_SIZE + 4); + + if(TRTP_RTCP_PACKET(self)->header->rc > 0){ + tsk_list_foreach(item, self->blocks){ + if(!item->data){ + continue; + } + if((ret = trtp_rtcp_rblock_serialize_to(TRTP_RTCP_RBLOCK(item->data), pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize the rblock"); + goto bail; + } + pdata += TRTP_RTCP_RBLOCK_SIZE; + size -= TRTP_RTCP_RBLOCK_SIZE; + } + } + + tsk_list_foreach(item, self->packets){ + if(!item->data){ + continue; + } + if((ret = trtp_rtcp_packet_serialize_to(TRTP_RTCP_PACKET(item->data), pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize packet"); + goto bail; + } + pdata += TRTP_RTCP_PACKET(item->data)->header->length_in_bytes; + size -= TRTP_RTCP_PACKET(item->data)->header->length_in_bytes; + } + +bail: + return ret; +} + +tsk_size_t trtp_rtcp_report_rr_get_size(const trtp_rtcp_report_rr_t* self) +{ + tsk_size_t size; + const tsk_list_item_t* item; + + if(!self || !TRTP_RTCP_PACKET(self)->header){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + + size = TRTP_RTCP_PACKET(self)->header->length_in_bytes; + if(TRTP_RTCP_PACKET(self)->header->rc > 0){ + tsk_list_foreach(item, self->blocks){ + if(item->data){ + size += TRTP_RTCP_RBLOCK_SIZE; + } + } + } + tsk_list_foreach(item, self->packets){ + if(item->data && TRTP_RTCP_PACKET(item->data)->header){ + size += TRTP_RTCP_PACKET(item->data)->header->length_in_bytes; + } + } + + return size; +}
\ No newline at end of file diff --git a/tinyRTP/src/rtcp/trtp_rtcp_report_sdes.c b/tinyRTP/src/rtcp/trtp_rtcp_report_sdes.c new file mode 100644 index 0000000..4f5e944 --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_report_sdes.c @@ -0,0 +1,214 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinyrtp/rtcp/trtp_rtcp_report_sdes.h" +#include "tinyrtp/rtcp/trtp_rtcp_header.h" + +#include "tsk_debug.h" + +/* +6.5 SDES: Source Description RTCP Packet + 0 1 2 + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| SC | PT=SDES=202 | length | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +chunk | SSRC/CSRC_1 | + 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SDES items | + | ... | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +chunk | SSRC/CSRC_2 | + 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SDES items | + | ... | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +*/ + +static tsk_object_t* trtp_rtcp_report_sdes_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_report_sdes_t *sdes = self; + if(sdes){ + sdes->chuncks = tsk_list_create(); + } + return self; +} +static tsk_object_t* trtp_rtcp_report_sdes_dtor(tsk_object_t * self) +{ + trtp_rtcp_report_sdes_t *sdes = self; + if(sdes){ + // deinit base + trtp_rtcp_packet_deinit(TRTP_RTCP_PACKET(sdes)); + // deinit self + TSK_OBJECT_SAFE_FREE(sdes->chuncks); + } + + return self; +} +static const tsk_object_def_t trtp_rtcp_report_sdes_def_s = +{ + sizeof(trtp_rtcp_report_sdes_t), + trtp_rtcp_report_sdes_ctor, + trtp_rtcp_report_sdes_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_report_sdes_def_t = &trtp_rtcp_report_sdes_def_s; + + +trtp_rtcp_report_sdes_t* trtp_rtcp_report_sdes_create_null() +{ + trtp_rtcp_report_sdes_t* sdes; + if((sdes = (trtp_rtcp_report_sdes_t*)tsk_object_new(trtp_rtcp_report_sdes_def_t))){ + trtp_rtcp_packet_init(TRTP_RTCP_PACKET(sdes), TRTP_RTCP_HEADER_VERSION_DEFAULT, 0, 0, trtp_rtcp_packet_type_sdes, TRTP_RTCP_HEADER_SIZE); + } + return sdes; +} + +trtp_rtcp_report_sdes_t* trtp_rtcp_report_sdes_create(trtp_rtcp_header_t* header) +{ + trtp_rtcp_report_sdes_t* sdes; + if((sdes = (trtp_rtcp_report_sdes_t*)tsk_object_new(trtp_rtcp_report_sdes_def_t))){ + TRTP_RTCP_PACKET(sdes)->header = tsk_object_ref(header); + } + return sdes; +} + +trtp_rtcp_report_sdes_t* trtp_rtcp_report_sdes_deserialize(const void* data, tsk_size_t _size) +{ + trtp_rtcp_report_sdes_t* sdes = tsk_null; + trtp_rtcp_header_t* header = tsk_null; + const uint8_t* pdata = (const uint8_t*)data; + int32_t size = (int32_t)_size; + + if(!data || size < TRTP_RTCP_HEADER_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + if(!(header = trtp_rtcp_header_deserialize(pdata, size))){ + TSK_DEBUG_ERROR("Failed to deserialize the header"); + goto bail; + } + if(header->length_in_bytes < (TRTP_RTCP_HEADER_SIZE + 4)){ + TSK_DEBUG_ERROR("Too short"); + goto bail; + } + + if(!(sdes = trtp_rtcp_report_sdes_create(header))){ + TSK_DEBUG_ERROR("Failed to create object"); + goto bail; + } + + pdata += TRTP_RTCP_HEADER_SIZE; + size -= TRTP_RTCP_HEADER_SIZE; + + // Chuncks + if(header->rc > 0){ + tsk_size_t i = 0, chunck_size; + trtp_rtcp_sdes_chunck_t* chunck; + while(i++ < header->rc && size > TRTP_RTCP_SDES_CHUNCK_MIN_SIZE){ + if((chunck = trtp_rtcp_sdes_chunck_deserialize(pdata, size))){ + chunck_size = trtp_rtcp_sdes_chunck_get_size(chunck); + if((size -= (int32_t)chunck_size)){ + pdata += chunck_size; + } + tsk_list_push_ascending_data(sdes->chuncks, (void**)&chunck); + continue; + } + break; + } + } + +bail: + TSK_OBJECT_SAFE_FREE(header); + return sdes; +} + +int trtp_rtcp_report_sdes_serialize_to(const trtp_rtcp_report_sdes_t* self, void* data, tsk_size_t size) +{ + int ret; + uint8_t* pdata = (uint8_t*)data; + if(!self || !data || size < trtp_rtcp_report_sdes_get_size(self)){ + return -1; + } + + if((ret = trtp_rtcp_header_serialize_to(TRTP_RTCP_PACKET(self)->header, pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize the header"); + return ret; + } + + pdata += (TRTP_RTCP_HEADER_SIZE); + size -= (TRTP_RTCP_HEADER_SIZE); + + if(TRTP_RTCP_PACKET(self)->header->rc > 0){ + const tsk_list_item_t* item; + tsk_size_t chunck_size; + const trtp_rtcp_sdes_chunck_t* chunck; + tsk_list_foreach(item, self->chuncks){ + if(!(chunck = item->data)){ + continue; + } + if((ret = trtp_rtcp_sdes_chunck_serialize_to(chunck, pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize SDES chunck"); + goto bail; + } + chunck_size = trtp_rtcp_sdes_chunck_get_size(chunck); + pdata += chunck_size; + size -= chunck_size; + } + } + +bail: + return ret; +} + +int trtp_rtcp_report_sdes_add_chunck(trtp_rtcp_report_sdes_t* self, trtp_rtcp_sdes_chunck_t* chunck) +{ + if(!self || !self->chuncks || !chunck){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + chunck = tsk_object_ref(chunck); + TRTP_RTCP_PACKET(self)->header->length_in_bytes += (uint32_t)trtp_rtcp_sdes_chunck_get_size(chunck); + TRTP_RTCP_PACKET(self)->header->length_in_words_minus1 = ((TRTP_RTCP_PACKET(self)->header->length_in_bytes >> 2) - 1) + + ((TRTP_RTCP_PACKET(self)->header->length_in_bytes & 0x03) ? 1 : 0); + ++TRTP_RTCP_PACKET(self)->header->rc; + tsk_list_push_back_data(self->chuncks, (void**)&chunck); + return 0; +} + +tsk_size_t trtp_rtcp_report_sdes_get_size(const trtp_rtcp_report_sdes_t* self) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + else{ + tsk_size_t size = TRTP_RTCP_HEADER_SIZE; + const tsk_list_item_t* item; + tsk_list_foreach(item, self->chuncks){ + size += trtp_rtcp_sdes_chunck_get_size(TRTP_RTCP_SDES_CHUNCK(item->data)); + } + return size; + } +} + diff --git a/tinyRTP/src/rtcp/trtp_rtcp_report_sr.c b/tinyRTP/src/rtcp/trtp_rtcp_report_sr.c new file mode 100644 index 0000000..5d8ceda --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_report_sr.c @@ -0,0 +1,270 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinyrtp/rtcp/trtp_rtcp_report_sr.h" +#include "tinyrtp/rtcp/trtp_rtcp_header.h" + +#include "tnet_endianness.h" + +#include "tsk_debug.h" + +#define TRTP_RTCP_PACKET_SR_MIN_SIZE (TRTP_RTCP_HEADER_SIZE + 4 + 20) + +static tsk_object_t* trtp_rtcp_report_sr_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_report_sr_t *sr = self; + if(sr){ + sr->blocks = tsk_list_create(); + sr->packets = tsk_list_create(); + } + return self; +} + +static tsk_object_t* trtp_rtcp_report_sr_dtor(tsk_object_t * self) +{ + trtp_rtcp_report_sr_t *sr = self; + if(sr){ + // deinit self + TSK_OBJECT_SAFE_FREE(sr->blocks); + TSK_OBJECT_SAFE_FREE(sr->packets); + // deinit base + trtp_rtcp_packet_deinit(TRTP_RTCP_PACKET(sr)); + } + + return self; +} + +static const tsk_object_def_t trtp_rtcp_report_sr_def_s = +{ + sizeof(trtp_rtcp_report_sr_t), + trtp_rtcp_report_sr_ctor, + trtp_rtcp_report_sr_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_report_sr_def_t = &trtp_rtcp_report_sr_def_s; + +trtp_rtcp_report_sr_t* trtp_rtcp_report_sr_create_null() +{ + trtp_rtcp_report_sr_t* sr; + if((sr = (trtp_rtcp_report_sr_t*)tsk_object_new(trtp_rtcp_report_sr_def_t))){ + trtp_rtcp_packet_init(TRTP_RTCP_PACKET(sr), TRTP_RTCP_HEADER_VERSION_DEFAULT, 0, 0, trtp_rtcp_packet_type_sr, (TRTP_RTCP_HEADER_SIZE + 4 + 20)); + } + return sr; +} + +trtp_rtcp_report_sr_t* trtp_rtcp_report_sr_create(struct trtp_rtcp_header_s* header) +{ + trtp_rtcp_report_sr_t* sr; + if((sr = (trtp_rtcp_report_sr_t*)tsk_object_new(trtp_rtcp_report_sr_def_t))){ + TRTP_RTCP_PACKET(sr)->header = tsk_object_ref(header); + } + return sr; +} + + +trtp_rtcp_report_sr_t* trtp_rtcp_report_sr_deserialize(const void* data, tsk_size_t _size) +{ + trtp_rtcp_report_sr_t* sr = tsk_null; + trtp_rtcp_header_t* header = tsk_null; + const uint8_t* pdata = (const uint8_t*)data; + int32_t size = (int32_t)_size; + + if(!data || size < TRTP_RTCP_PACKET_SR_MIN_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + if(!(header = trtp_rtcp_header_deserialize(pdata, size))){ + TSK_DEBUG_ERROR("Failed to deserialize the header"); + goto bail; + } + if(header->length_in_bytes < TRTP_RTCP_PACKET_SR_MIN_SIZE){ + TSK_DEBUG_ERROR("Too short"); + goto bail; + } + + if(!(sr = trtp_rtcp_report_sr_create(header))){ + TSK_DEBUG_ERROR("Failed to create object"); + goto bail; + } + + sr->ssrc = (uint32_t)tnet_ntohl_2(&pdata[4]); + pdata += (TRTP_RTCP_HEADER_SIZE + 4); + size -= (TRTP_RTCP_HEADER_SIZE + 4); + + // sender info + sr->sender_info.ntp_msw = (uint32_t)tnet_ntohl_2(&pdata[0]); + sr->sender_info.ntp_lsw = (uint32_t)tnet_ntohl_2(&pdata[4]); + sr->sender_info.rtp_timestamp = (uint32_t)tnet_ntohl_2(&pdata[8]); + sr->sender_info.sender_pcount = (uint32_t)tnet_ntohl_2(&pdata[12]); + sr->sender_info.sender_ocount = (uint32_t)tnet_ntohl_2(&pdata[16]); + pdata += 20; + size -= 20; + + // Blocks + if(header->rc > 0){ + tsk_size_t i = 0; + trtp_rtcp_rblock_t* rblock; + + while(i++ < header->rc && size >= TRTP_RTCP_RBLOCK_SIZE){ + if((rblock = trtp_rtcp_rblock_deserialize(pdata, size))){ + tsk_list_push_back_data(sr->blocks, (void**)&rblock); + } + pdata += TRTP_RTCP_RBLOCK_SIZE; + size -= TRTP_RTCP_RBLOCK_SIZE; + } + } + + // Other Packets + while(size > TRTP_RTCP_HEADER_SIZE){ + trtp_rtcp_packet_t* packet; + + if((packet = trtp_rtcp_packet_deserialize(pdata, size))){ + if((size -= packet->header->length_in_bytes) > 0){ + pdata += packet->header->length_in_bytes; + } + tsk_list_push_back_data(sr->packets, (void**)&packet); + continue; + } + break; + } + +bail: + TSK_OBJECT_SAFE_FREE(header); + return sr; +} + +int trtp_rtcp_report_sr_serialize_to(const trtp_rtcp_report_sr_t* self, void* data, tsk_size_t size) +{ + int ret; + const tsk_list_item_t* item; + uint8_t* pdata = (uint8_t*)data; + + if(!self || !data || size < trtp_rtcp_report_sr_get_size(self)){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if((ret = trtp_rtcp_header_serialize_to(TRTP_RTCP_PACKET(self)->header, pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize the header"); + return ret; + } + + pdata[TRTP_RTCP_HEADER_SIZE] = self->ssrc >> 24; + pdata[TRTP_RTCP_HEADER_SIZE + 1] = (self->ssrc >> 16) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 2] = (self->ssrc >> 8) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 3] = (self->ssrc & 0xFF); + pdata[TRTP_RTCP_HEADER_SIZE + 4] = self->sender_info.ntp_msw >> 24; + pdata[TRTP_RTCP_HEADER_SIZE + 5] = (self->sender_info.ntp_msw >> 16) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 6] = (self->sender_info.ntp_msw >> 8) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 7] = self->sender_info.ntp_msw & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 8] = self->sender_info.ntp_lsw >> 24; + pdata[TRTP_RTCP_HEADER_SIZE + 9] = (self->sender_info.ntp_lsw >> 16) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 10] = (self->sender_info.ntp_lsw >> 8) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 11] = self->sender_info.ntp_lsw & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 12] = self->sender_info.rtp_timestamp >> 24; + pdata[TRTP_RTCP_HEADER_SIZE + 13] = (self->sender_info.rtp_timestamp >> 16) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 14] = (self->sender_info.rtp_timestamp >> 8) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 15] = self->sender_info.rtp_timestamp & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 16] = self->sender_info.sender_pcount >> 24; + pdata[TRTP_RTCP_HEADER_SIZE + 17] = (self->sender_info.sender_pcount >> 16) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 18] = (self->sender_info.sender_pcount >> 8) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 19] = self->sender_info.sender_pcount & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 20] = self->sender_info.sender_ocount >> 24; + pdata[TRTP_RTCP_HEADER_SIZE + 21] = (self->sender_info.sender_ocount >> 16) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 22] = (self->sender_info.sender_ocount >> 8) & 0xFF; + pdata[TRTP_RTCP_HEADER_SIZE + 23] = self->sender_info.sender_ocount & 0xFF; + + pdata += (TRTP_RTCP_HEADER_SIZE + 4 + 20); + size -= (TRTP_RTCP_HEADER_SIZE + 4 + 20); + + if(TRTP_RTCP_PACKET(self)->header->rc > 0){ + tsk_list_foreach(item, self->blocks){ + if(!item->data){ + continue; + } + if((ret = trtp_rtcp_rblock_serialize_to(TRTP_RTCP_RBLOCK(item->data), pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize the rblock"); + goto bail; + } + pdata += TRTP_RTCP_RBLOCK_SIZE; + size -= TRTP_RTCP_RBLOCK_SIZE; + } + } + + tsk_list_foreach(item, self->packets){ + if(!item->data){ + continue; + } + if((ret = trtp_rtcp_packet_serialize_to(TRTP_RTCP_PACKET(item->data), pdata, size))){ + TSK_DEBUG_ERROR("Failed to serialize packet"); + goto bail; + } + pdata += TRTP_RTCP_PACKET(item->data)->header->length_in_bytes; + size -= TRTP_RTCP_PACKET(item->data)->header->length_in_bytes; + } + +bail: + return ret; +} + +int trtp_rtcp_report_sr_add_block(trtp_rtcp_report_sr_t* self, trtp_rtcp_rblock_t* rblock) +{ + if(!self || !TRTP_RTCP_PACKET(self)->header || !rblock){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + rblock = tsk_object_ref(rblock); + tsk_list_push_back_data(self->blocks, (void**)&rblock); + ++TRTP_RTCP_PACKET(self)->header->rc; + TRTP_RTCP_PACKET(self)->header->length_in_bytes += TRTP_RTCP_RBLOCK_SIZE; + TRTP_RTCP_PACKET(self)->header->length_in_words_minus1 = ((TRTP_RTCP_PACKET(self)->header->length_in_bytes >> 2) - 1) + + ((TRTP_RTCP_PACKET(self)->header->length_in_bytes & 0x03) ? 1 : 0); + return 0; +} + +tsk_size_t trtp_rtcp_report_sr_get_size(const trtp_rtcp_report_sr_t* self) +{ + tsk_size_t size; + const tsk_list_item_t* item; + + if(!self || !TRTP_RTCP_PACKET(self)->header){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + + size = TRTP_RTCP_PACKET(self)->header->length_in_bytes; + //if(TRTP_RTCP_PACKET(self)->header->rc > 0){ + // tsk_list_foreach(item, self->blocks){ + // if(item->data){ + // size += TRTP_RTCP_RBLOCK_SIZE; + // } + // } + //} + tsk_list_foreach(item, self->packets){ + if(item->data && TRTP_RTCP_PACKET(item->data)->header){ + size += TRTP_RTCP_PACKET(item->data)->header->length_in_bytes; + } + } + + return size; +} + diff --git a/tinyRTP/src/rtcp/trtp_rtcp_report_xr.c b/tinyRTP/src/rtcp/trtp_rtcp_report_xr.c new file mode 100644 index 0000000..5fb2697 --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_report_xr.c @@ -0,0 +1,21 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/
\ No newline at end of file diff --git a/tinyRTP/src/rtcp/trtp_rtcp_sdes_chunck.c b/tinyRTP/src/rtcp/trtp_rtcp_sdes_chunck.c new file mode 100644 index 0000000..9104580 --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_sdes_chunck.c @@ -0,0 +1,190 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinyrtp/rtcp/trtp_rtcp_sdes_chunck.h" + +#include "tnet_endianness.h" + +#include "tsk_debug.h" + +/* +Each chunk consists of an SSRC/CSRC identifier followed by a list of + zero or more items, which carry information about the SSRC/CSRC. + Each chunk starts on a 32-bit boundary. Each item consists of an 8- + bit type field, an 8-bit octet count describing the length of the + text (thus, not including this two-octet header), and the text + itself. Note that the text can be no longer than 255 octets, but + this is consistent with the need to limit RTCP bandwidth consumption. + ++=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +| SSRC/CSRC_1 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| SDES items | +| ... | ++=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +*/ + +static tsk_object_t* trtp_rtcp_sdes_chunck_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_sdes_chunck_t *chunck = self; + if(chunck){ + chunck->items = tsk_list_create(); + } + return self; +} +static tsk_object_t* trtp_rtcp_sdes_chunck_dtor(tsk_object_t * self) +{ + trtp_rtcp_sdes_chunck_t *chunck = self; + if(chunck){ + TSK_OBJECT_SAFE_FREE(chunck->items); + } + + return self; +} +static const tsk_object_def_t trtp_rtcp_sdes_chunck_def_s = +{ + sizeof(trtp_rtcp_sdes_chunck_t), + trtp_rtcp_sdes_chunck_ctor, + trtp_rtcp_sdes_chunck_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_sdes_chunck_def_t = &trtp_rtcp_sdes_chunck_def_s; + + + +trtp_rtcp_sdes_chunck_t* trtp_rtcp_sdes_chunck_create_null() +{ + return tsk_object_new(trtp_rtcp_sdes_chunck_def_t); +} + +trtp_rtcp_sdes_chunck_t* trtp_rtcp_sdes_chunck_create(uint32_t ssrc) +{ + trtp_rtcp_sdes_chunck_t* chunck; + if((chunck = trtp_rtcp_sdes_chunck_create_null())){ + chunck->ssrc = ssrc; + } + return chunck; +} + +trtp_rtcp_sdes_chunck_t* trtp_rtcp_sdes_chunck_deserialize(const void* data, tsk_size_t size) +{ + trtp_rtcp_sdes_chunck_t* chunck = tsk_null; + const uint8_t* pdata = (uint8_t*)data; + const uint8_t* pend; + if(!data || size < TRTP_RTCP_SDES_CHUNCK_MIN_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + pend = (pdata + size); + if((chunck = trtp_rtcp_sdes_chunck_create_null())){ + trtp_rtcp_sdes_item_t* item; + tsk_bool_t is_last_item = tsk_false; + // SSRC/CSRC + chunck->ssrc = (uint32_t)tnet_ntohl_2(pdata); + pdata += TRTP_RTCP_SDES_CHUNCK_SSRC_OR_CSRC_SIZE; + while((pdata < pend) && !is_last_item){ + if((item = trtp_rtcp_sdes_item_deserialize(pdata, (pend-pdata)))){ + is_last_item = (item->type == trtp_rtcp_sdes_item_type_end); + pdata += trtp_rtcp_sdes_item_get_size(item); + tsk_list_push_back_data(chunck->items, (void**)&item); + } + else{ + TSK_DEBUG_ERROR("Failed to deserialize sdes item"); + break; + } + } + } + else{ + TSK_DEBUG_ERROR("Failed to create new sdes_chunck object"); + return tsk_null; + } + + return chunck; +} + +int trtp_rtcp_sdes_chunck_serialize_to(const trtp_rtcp_sdes_chunck_t* self, void* data, tsk_size_t size) +{ + uint8_t* pdata = (uint8_t*)data; + const tsk_list_item_t* item; + const trtp_rtcp_sdes_item_t* sdes_item; + tsk_size_t sdes_item_size; + int ret = 0; + + if(!self || !data || size < trtp_rtcp_sdes_chunck_get_size(self)){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + pdata[0] = self->ssrc >> 24; + pdata[1] = (self->ssrc >> 16) & 0xFF; + pdata[2] = (self->ssrc >> 8) & 0xFF; + pdata[3] = (self->ssrc & 0xFF); + pdata += 4; + + tsk_list_foreach(item, self->items){ + if(!(sdes_item = TRTP_RTCP_SDES_ITEM(item->data))){ + continue; + } + if((ret = trtp_rtcp_sdes_item_serialize_to(sdes_item, pdata, size))){ + TSK_DEBUG_ERROR("SDES item serialization failed"); + goto bail; + } + sdes_item_size = trtp_rtcp_sdes_item_get_size(sdes_item); + pdata += sdes_item_size; size -= sdes_item_size; + } + +bail: + return ret; +} + +int trtp_rtcp_sdes_chunck_add_item(trtp_rtcp_sdes_chunck_t* self, trtp_rtcp_sdes_item_type_t type, const void* data, uint8_t length) +{ + trtp_rtcp_sdes_item_t *item; + if(!self || !self->items){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if((item = trtp_rtcp_sdes_item_create(type, data, length))){ + tsk_list_push_back_data(self->items, (void**)&item); + } + return 0; +} + +tsk_size_t trtp_rtcp_sdes_chunck_get_size(const trtp_rtcp_sdes_chunck_t* self) +{ + const tsk_list_item_t* item; + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + else{ + tsk_size_t size = TRTP_RTCP_SDES_CHUNCK_SSRC_OR_CSRC_SIZE; + tsk_list_foreach(item, self->items){ + size += trtp_rtcp_sdes_item_get_size(TRTP_RTCP_SDES_ITEM(item->data)); + } + if(size & 0x03){//Each chunk starts on a 32-bit boundary + size += (4 - (size & 0x03)); + } + return size; + } +} + diff --git a/tinyRTP/src/rtcp/trtp_rtcp_sdes_item.c b/tinyRTP/src/rtcp/trtp_rtcp_sdes_item.c new file mode 100644 index 0000000..7f22537 --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_sdes_item.c @@ -0,0 +1,148 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include "tinyrtp/rtcp/trtp_rtcp_sdes_item.h" +#include "tinyrtp/rtcp/trtp_rtcp_packet.h" + +#include "tnet_endianness.h" + +#include "tsk_string.h" +#include "tsk_memory.h" +#include "tsk_debug.h" + +#include <string.h> /* strlen() */ + +static tsk_object_t* trtp_rtcp_sdes_item_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_sdes_item_t *item = self; + if(item){ + } + return self; +} +static tsk_object_t* trtp_rtcp_sdes_item_dtor(tsk_object_t * self) +{ + trtp_rtcp_sdes_item_t *item = self; + if(item){ + TSK_OBJECT_SAFE_FREE(item->data); + } + + return self; +} +static const tsk_object_def_t trtp_rtcp_sdes_item_def_s = +{ + sizeof(trtp_rtcp_sdes_item_t), + trtp_rtcp_sdes_item_ctor, + trtp_rtcp_sdes_item_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_sdes_item_def_t = &trtp_rtcp_sdes_item_def_s; + + +trtp_rtcp_sdes_item_t* _trtp_rtcp_sdes_item_create_null(trtp_rtcp_sdes_item_type_t type) +{ + return tsk_object_new(trtp_rtcp_sdes_item_def_t); +} + +trtp_rtcp_sdes_item_t* trtp_rtcp_sdes_item_create(trtp_rtcp_sdes_item_type_t type, const void* data, uint8_t length) +{ + trtp_rtcp_sdes_item_t* item; + if(!(item = _trtp_rtcp_sdes_item_create_null(type))){ + TSK_DEBUG_ERROR("Failed to create new SDES item"); + return tsk_null; + } + item->type = type; + if(data && length){ + item->data = tsk_buffer_create(data, length); + } + + return item; +} + +trtp_rtcp_sdes_item_t* trtp_rtcp_sdes_item_deserialize(const void* data, tsk_size_t size) +{ + const uint8_t* pdata = (const uint8_t*)data; + + if(!data || !size){ + TSK_DEBUG_ERROR("Invlaid parameter"); + return tsk_null; + } + + if(pdata[0] == trtp_rtcp_sdes_item_type_end){ + return trtp_rtcp_sdes_item_create(trtp_rtcp_sdes_item_type_end, tsk_null, 0); + } + + if(size < TRTP_RTCP_SDES_ITEM_MIN_SIZE || size < (tsk_size_t)(pdata[1] + 2)){ + TSK_DEBUG_ERROR("Too short"); + return tsk_null; + } + + return trtp_rtcp_sdes_item_create((trtp_rtcp_sdes_item_type_t)pdata[0], &pdata[2], pdata[1]); +} + +tsk_buffer_t* trtp_rtcp_sdes_item_serialize(const trtp_rtcp_sdes_item_t* self) +{ + tsk_buffer_t*buffer = tsk_null; + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + if((buffer = tsk_buffer_create(tsk_null, trtp_rtcp_sdes_item_get_size(self)))){ + if(trtp_rtcp_sdes_item_serialize_to(self, buffer->data, buffer->size) != 0){ + TSK_OBJECT_SAFE_FREE(buffer); + } + } + return buffer; +} + +int trtp_rtcp_sdes_item_serialize_to(const trtp_rtcp_sdes_item_t* self, void* data, tsk_size_t size) +{ + if(!self || !data || (size < trtp_rtcp_sdes_item_get_size(self))){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if(self->type == trtp_rtcp_sdes_item_type_end){ + ((uint8_t*)data)[0] = trtp_rtcp_sdes_item_type_end; + } + else{ + ((uint8_t*)data)[0] = self->type; + if(self->data){ + ((uint8_t*)data)[1] = (uint8_t)self->data->size; + memcpy(&((uint8_t*)data)[2], self->data->data, self->data->size); + } + else{ + ((uint8_t*)data)[1] = 0; + } + } + return 0; +} + +tsk_size_t trtp_rtcp_sdes_item_get_size(const trtp_rtcp_sdes_item_t* self) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + switch(self->type){ + case trtp_rtcp_sdes_item_type_end: return 1; + default: return 2 + (self->data ? self->data->size : 0); + } +} + diff --git a/tinyRTP/src/rtcp/trtp_rtcp_session.c b/tinyRTP/src/rtcp/trtp_rtcp_session.c new file mode 100644 index 0000000..3172278 --- /dev/null +++ b/tinyRTP/src/rtcp/trtp_rtcp_session.c @@ -0,0 +1,1584 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp_rtcp_session.c + * @brief RTCP session. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + + */ +#include "tinyrtp/rtcp/trtp_rtcp_session.h" +#include "tinyrtp/rtcp/trtp_rtcp_packet.h" +#include "tinyrtp/rtcp/trtp_rtcp_header.h" +#include "tinyrtp/rtcp/trtp_rtcp_report_rr.h" +#include "tinyrtp/rtcp/trtp_rtcp_report_sr.h" +#include "tinyrtp/rtcp/trtp_rtcp_report_sdes.h" +#include "tinyrtp/rtcp/trtp_rtcp_report_bye.h" +#include "tinyrtp/rtcp/trtp_rtcp_report_fb.h" +#include "tinyrtp/rtp/trtp_rtp_packet.h" + +#include "ice/tnet_ice_ctx.h" +#include "turn/tnet_turn_session.h" +#include "tnet_transport.h" + +#include "tnet_utils.h" + +#include "tsk_string.h" +#include "tsk_md5.h" +#include "tsk_list.h" +#include "tsk_time.h" +#include "tsk_timer.h" +#include "tsk_safeobj.h" +#include "tsk_memory.h" +#include "tsk_debug.h" + +#include <stdlib.h> +#include <string.h> +#include <limits.h> /* INT_MAX */ + +#ifdef _MSC_VER +static double drand48() { return (((double)rand()) / RAND_MAX); } +static void srand48(long sv) { srand((unsigned int) sv); } +#endif + +#define RTCP_BW (160 * 50) // FIXME: default bandwidth (octet/second) +#define CODEC_RATE 8000 // FIXME +#define RTP_SEQ_MOD (1 << 16) +#define MAX_DROPOUT 3000 +#define MAX_MISORDER 100 +#define MIN_SEQUENTIAL 2 + +typedef double time_tp; +typedef void* packet_; + +typedef enum event_ +{ + EVENT_BYE, + EVENT_REPORT, + EVENT_RTP +} +event_; + +typedef enum PacketType_ +{ + PACKET_RTCP_REPORT, + PACKET_BYE, + PACKET_RTP, +} +PacketType_; + +#define TypeOfEvent(e) (e) + +#define TRTP_RTCP_SOURCE(self) ((trtp_rtcp_source_t*)self) + +typedef struct trtp_rtcp_source_s +{ + TSK_DECLARE_OBJECT; + + uint32_t ssrc; /* source's ssrc */ + uint16_t max_seq; /* highest seq. number seen */ + uint32_t cycles; /* shifted count of seq. number cycles */ + uint32_t base_seq; /* base seq number */ + uint32_t bad_seq; /* last 'bad' seq number + 1 */ + uint32_t probation; /* sequ. packets till source is valid */ + uint32_t received; /* packets received */ + uint32_t expected_prior; /* packet expected at last interval */ + uint32_t received_prior; /* packet received at last interval */ + uint32_t transit; /* relative trans time for prev pkt */ + double jitter; /* estimated jitter */ + + uint32_t base_ts; /* base timestamp */ + uint32_t max_ts; /* highest timestamp number seen */ + uint32_t rate; /* codec sampling rate */ + + uint32_t ntp_msw; /* last received NTP timestamp from RTCP sender */ + uint32_t ntp_lsw; /* last received NTP timestamp from RTCP sender */ + uint64_t dlsr; /* delay since last SR */ +} +trtp_rtcp_source_t; +typedef tsk_list_t trtp_rtcp_sources_L_t; /**< List of @ref trtp_rtcp_header_t elements */ + +static tsk_object_t* trtp_rtcp_source_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_source_t *source = self; + if(source){ + } + return self; +} +static tsk_object_t* trtp_rtcp_source_dtor(tsk_object_t * self) +{ + trtp_rtcp_source_t *source = self; + if(source){ + } + return self; +} +static const tsk_object_def_t trtp_rtcp_source_def_s = +{ + sizeof(trtp_rtcp_source_t), + trtp_rtcp_source_ctor, + trtp_rtcp_source_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_source_def_t = &trtp_rtcp_source_def_s; + +static int _trtp_rtcp_source_init_seq(trtp_rtcp_source_t* self, uint16_t seq, uint32_t ts); +static tsk_bool_t _trtp_rtcp_source_update_seq(trtp_rtcp_source_t* self, uint16_t seq, uint32_t ts); + +static int __pred_find_source_by_ssrc(const tsk_list_item_t *item, const void *pssrc) +{ + if(item && item->data){ + trtp_rtcp_source_t *source = item->data; + return source->ssrc - *((uint32_t*)pssrc); + } + return -1; +} + +static trtp_rtcp_source_t* _trtp_rtcp_source_create(uint32_t ssrc, uint16_t seq, uint32_t ts) +{ + trtp_rtcp_source_t* source; + if(!(source = tsk_object_new(trtp_rtcp_source_def_t))){ + TSK_DEBUG_ERROR("Failed to create source object"); + return tsk_null; + } + + _trtp_rtcp_source_init_seq(source, seq, ts); + source->ssrc = ssrc; + source->max_seq = seq - 1; + source->probation = MIN_SEQUENTIAL; + source->rate = CODEC_RATE;//FIXME + + return source; +} + +static int _trtp_rtcp_source_init_seq(trtp_rtcp_source_t* self, uint16_t seq, uint32_t ts) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->base_seq = seq; + self->max_seq = seq; + self->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */ + self->cycles = 0; + self->received = 0; + self->received_prior = 0; + self->expected_prior = 0; + self->base_ts = ts; + self->max_ts = ts; + return 0; +} + +static tsk_bool_t _trtp_rtcp_source_update_seq(trtp_rtcp_source_t* self, uint16_t seq, uint32_t ts) +{ + uint16_t udelta; + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_false; + } + + udelta = seq - self->max_seq; + + /* + * Source is not valid until MIN_SEQUENTIAL packets with + * sequential sequence numbers have been received. + */ + if (self->probation) { + /* packet is in sequence */ + if (seq == self->max_seq + 1) { + self->probation--; + self->max_seq = seq; + self->max_ts = ts; + if (self->probation == 0) { + _trtp_rtcp_source_init_seq(self, seq, ts); + self->received++; + return tsk_true; + } + } else { + self->probation = MIN_SEQUENTIAL - 1; + self->max_seq = seq; + self->max_ts = ts; + } + return tsk_false; + } else if (udelta < MAX_DROPOUT) { + /* in order, with permissible gap */ + if (seq < self->max_seq) { + /* + * Sequence number wrapped - count another 64K cycle. + */ + self->cycles += RTP_SEQ_MOD; + } + self->max_seq = seq; + self->max_ts = ts; + } else if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) { + /* the sequence number made a very large jump */ + if (seq == self->bad_seq) { + /* + * Two sequential packets -- assume that the other side + * restarted without telling us so just re-sync + * (i.e., pretend this was the first packet). + */ + _trtp_rtcp_source_init_seq(self, seq, ts); + } + else { + self->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1); + return tsk_false; + } + } else { + /* duplicate or reordered packet */ + } + self->received++; + return tsk_true; +} + +static tsk_bool_t _trtp_rtcp_source_is_probed(const trtp_rtcp_source_t* self) +{ + return (self && self->probation == 0); +} + + + + + + + +typedef time_tp (*tc_f)(); +static time_tp _trtp_rtcp_session_tc() { return (time_tp)tsk_time_now(); } + +typedef struct trtp_rtcp_session_s +{ + TSK_DECLARE_OBJECT; + + tsk_bool_t is_started; + tnet_fd_t local_fd; + struct tnet_transport_s* transport; // not starter -> do not stop + const struct sockaddr * remote_addr; + struct tnet_ice_ctx_s* ice_ctx; // not starter -> do not stop + tsk_bool_t is_ice_turn_active; + + const void* callback_data; + trtp_rtcp_cb_f callback; + + int32_t app_bw_max_upload; // application specific (kbps) + int32_t app_bw_max_download; // application specific (kbps) + + struct{ + tsk_timer_manager_handle_t* handle_global; + tsk_timer_id_t id_report; + tsk_timer_id_t id_bye; + } timer; + + trtp_rtcp_source_t* source_local; /**< local source */ + trtp_rtcp_report_sdes_t* sdes; + uint64_t time_start; /**< Start time in millis (NOT in NTP unit yet) */ + + // <RTCP-FB> + uint8_t fir_seqnr; + // </RTCP-FB> + + // <sender> + char* cname; + uint32_t packets_count; + uint32_t octets_count; + // </sender> + + // <others> + time_tp tp; /**< the last time an RTCP packet was transmitted; */ + tc_f tc; /**< the current time */ + time_tp tn; /**< the next scheduled transmission time of an RTCP packet */ + int32_t pmembers; /**< the estimated number of session members at the time tn was last recomputed */ + int32_t members; /**< the most current estimate for the number of session members */ + int32_t senders; /**< the most current estimate for the number of senders in the session */ + double rtcp_bw; /**< The target RTCP bandwidth, i.e., the total bandwidth + that will be used for RTCP packets by all members of this session, + in octets per second. This will be a specified fraction of the + "session bandwidth" parameter supplied to the application at + startup*/ + tsk_bool_t we_sent; /**< Flag that is true if the application has sent data since the 2nd previous RTCP report was transmitted */ + double avg_rtcp_size; /**< The average compound RTCP packet size, in octets, + over all RTCP packets sent and received by this participant. The + size includes lower-layer transport and network protocol headers + (e.g., UDP and IP) as explained in Section 6.2*/ + tsk_bool_t initial; /**< Flag that is true if the application has not yet sent an RTCP packet */ + // </others> + + trtp_rtcp_sources_L_t *sources; + + TSK_DECLARE_SAFEOBJ; + +#if HAVE_SRTP + struct{ + const srtp_t* session; + } srtp; +#endif +} +trtp_rtcp_session_t; + +static tsk_object_t* trtp_rtcp_session_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtcp_session_t *session = self; + if(session){ + session->app_bw_max_upload = INT_MAX; // INT_MAX or <=0 means undefined + session->app_bw_max_download = INT_MAX; // INT_MAX or <=0 means undefined + session->sources = tsk_list_create(); + session->timer.id_report = TSK_INVALID_TIMER_ID; + session->timer.id_bye = TSK_INVALID_TIMER_ID; + session->tc = _trtp_rtcp_session_tc; + // get a handle for the global timer manager + session->timer.handle_global = tsk_timer_mgr_global_ref(); + tsk_safeobj_init(session); + } + return self; +} +static tsk_object_t* trtp_rtcp_session_dtor(tsk_object_t * self) +{ + trtp_rtcp_session_t *session = self; + if(session){ + trtp_rtcp_session_stop(session); + + TSK_OBJECT_SAFE_FREE(session->sources); + TSK_OBJECT_SAFE_FREE(session->source_local); + TSK_OBJECT_SAFE_FREE(session->sdes); + TSK_OBJECT_SAFE_FREE(session->ice_ctx); // not starter -> do not stop + TSK_FREE(session->cname); + TSK_OBJECT_SAFE_FREE(session->transport); // not starter -> do not stop + // release the handle for the global timer manager + tsk_timer_mgr_global_unref(&session->timer.handle_global); + + tsk_safeobj_deinit(session); + } + return self; +} +static const tsk_object_def_t trtp_rtcp_session_def_s = +{ + sizeof(trtp_rtcp_session_t), + trtp_rtcp_session_ctor, + trtp_rtcp_session_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtcp_session_def_t = &trtp_rtcp_session_def_s; + + +static tsk_bool_t _trtp_rtcp_session_have_source(trtp_rtcp_session_t* self, uint32_t ssrc); +static trtp_rtcp_source_t* _trtp_rtcp_session_find_source(trtp_rtcp_session_t* self, uint32_t ssrc); +static trtp_rtcp_source_t* _trtp_rtcp_session_find_or_add_source(trtp_rtcp_session_t* self, uint32_t ssrc, uint16_t seq_if_add, uint32_t ts_id_add); +static int _trtp_rtcp_session_add_source(trtp_rtcp_session_t* self, trtp_rtcp_source_t* source); +static int _trtp_rtcp_session_add_source_2(trtp_rtcp_session_t* self, uint32_t ssrc, uint16_t seq, uint32_t ts, tsk_bool_t *added); +static int _trtp_rtcp_session_remove_source(trtp_rtcp_session_t* self, uint32_t ssrc, tsk_bool_t *removed); +static tsk_size_t _trtp_rtcp_session_send_pkt(trtp_rtcp_session_t* self, trtp_rtcp_packet_t* pkt); +static tsk_size_t _trtp_rtcp_session_send_raw(trtp_rtcp_session_t* self, const void* data, tsk_size_t size); +static int _trtp_rtcp_session_timer_callback(const void* arg, tsk_timer_id_t timer_id); + +static void Schedule(trtp_rtcp_session_t* session, double tn, event_ e); +static void OnReceive(trtp_rtcp_session_t* session, const packet_ p, event_ e, tsk_size_t ReceivedPacketSize); +static void OnExpire(trtp_rtcp_session_t* session, event_ e); +static void SendBYEPacket(trtp_rtcp_session_t* session, event_ e); + +trtp_rtcp_session_t* trtp_rtcp_session_create(uint32_t ssrc, const char* cname) +{ + trtp_rtcp_session_t* session; + + if(!(session = tsk_object_new(trtp_rtcp_session_def_t))){ + TSK_DEBUG_ERROR("Failed to create new session object"); + return tsk_null; + } + + // RFC 3550 - 6.3.2 Initialization + if(!(session->source_local = _trtp_rtcp_source_create(ssrc, 0, 0))){ + TSK_DEBUG_ERROR("Failed to create new local source"); + TSK_OBJECT_SAFE_FREE(session); + goto bail; + } + _trtp_rtcp_session_add_source(session, session->source_local); + session->initial = tsk_true; + session->we_sent = tsk_false; + session->senders = 1; + session->members = 1; + session->rtcp_bw = RTCP_BW;//FIXME: as parameter from the code, Also added possiblities to update this value + session->cname = tsk_strdup(cname); + +bail: + return session; +} + +struct trtp_rtcp_session_s* trtp_rtcp_session_create_2(struct tnet_ice_ctx_s* ice_ctx, uint32_t ssrc, const char* cname) +{ + struct trtp_rtcp_session_s* session = trtp_rtcp_session_create(ssrc, cname); + if (session) { + if ((session->ice_ctx = tsk_object_ref(ice_ctx))) { + session->is_ice_turn_active = tnet_ice_ctx_is_turn_rtcp_active(session->ice_ctx); + } + } + return session; +} + +int trtp_rtcp_session_set_callback(trtp_rtcp_session_t* self, trtp_rtcp_cb_f callback, const void* callback_data) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + tsk_safeobj_lock(self); + self->callback = callback; + self->callback_data = callback_data; + tsk_safeobj_unlock(self); + return 0; +} + +#if HAVE_SRTP +int trtp_rtcp_session_set_srtp_sess(trtp_rtcp_session_t* self, const srtp_t* session) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tsk_safeobj_lock(self); + self->srtp.session = session; + tsk_safeobj_unlock(self); + + return 0; +} +#endif + +int trtp_rtcp_session_set_app_bandwidth_max(trtp_rtcp_session_t* self, int32_t bw_upload_kbps, int32_t bw_download_kbps) +{ + trtp_rtcp_report_rr_t* rr; + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tsk_safeobj_lock(self); + + self->app_bw_max_upload = bw_upload_kbps; + self->app_bw_max_download = bw_download_kbps; + + if(self->is_started && self->source_local && self->app_bw_max_download > 0 && self->app_bw_max_download != INT_MAX){ // INT_MAX or <=0 means undefined + tsk_list_item_t* item; + uint32_t media_ssrc_list[256] = {0}; + uint32_t media_ssrc_list_count = 0; + // retrieve sources as array + tsk_list_foreach(item, self->sources){ + if(!item->data){ + continue; + } + if((media_ssrc_list_count + 1) < sizeof(media_ssrc_list)/sizeof(media_ssrc_list[0])){ + media_ssrc_list[media_ssrc_list_count++] = TRTP_RTCP_SOURCE(item->data)->ssrc; + } + } + // create RTCP-RR packet and send it over the network + if(media_ssrc_list_count > 0 && (rr = trtp_rtcp_report_rr_create_2(self->source_local->ssrc))){ + // app_bw_max_download unit is kbps while create_afb_remb() expect bps + trtp_rtcp_report_psfb_t* psfb_afb_remb = trtp_rtcp_report_psfb_create_afb_remb(self->source_local->ssrc/*sender SSRC*/, media_ssrc_list, media_ssrc_list_count, (self->app_bw_max_download * 1024)); + if(psfb_afb_remb){ + TSK_DEBUG_INFO("Packing RTCP-AFB-REMB (bw_dwn=%d kbps) for outgoing RTCP-RR", self->app_bw_max_download); + if(trtp_rtcp_packet_add_packet((trtp_rtcp_packet_t*)rr, (trtp_rtcp_packet_t*)psfb_afb_remb, tsk_false) == 0){ + _trtp_rtcp_session_send_pkt(self, (trtp_rtcp_packet_t*)rr); + } + TSK_OBJECT_SAFE_FREE(psfb_afb_remb); + } + TSK_OBJECT_SAFE_FREE(rr); + } + } + + tsk_safeobj_unlock(self); + + return 0; +} + +int trtp_rtcp_session_start(trtp_rtcp_session_t* self, tnet_fd_t local_fd, const struct sockaddr * remote_addr) +{ + int ret; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if(self->is_started){ + TSK_DEBUG_WARN("Already started"); + return 0; + } + + // start global timer manager + if((ret = tsk_timer_manager_start(self->timer.handle_global))){ + TSK_DEBUG_ERROR("Failed to start timer"); + return ret; + } + + self->local_fd = local_fd; + self->remote_addr = remote_addr; + + // Send Initial RR (mandatory) + Schedule(self, 0., EVENT_REPORT); + + // set start time + self->time_start = tsk_time_now(); + + self->is_started = tsk_true; + + return ret; +} + +int trtp_rtcp_session_stop(trtp_rtcp_session_t* self) +{ + int ret = 0; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(self->is_started){ + // send BYE synchronous way + SendBYEPacket(self, EVENT_REPORT); + + // this is a global timer shared by many components -> stopping it won't remove + // all scheduled items as it could continue running if still used + tsk_safeobj_lock(self); // must + if(TSK_TIMER_ID_IS_VALID(self->timer.id_bye)){ + tsk_timer_manager_cancel(self->timer.handle_global, self->timer.id_bye); + self->timer.id_bye = TSK_INVALID_TIMER_ID; + } + if(TSK_TIMER_ID_IS_VALID(self->timer.id_report)){ + tsk_timer_manager_cancel(self->timer.handle_global, self->timer.id_report); + self->timer.id_report = TSK_INVALID_TIMER_ID; + } + tsk_safeobj_unlock(self); + self->is_started = tsk_false; + } + + return ret; +} + +int trtp_rtcp_session_process_rtp_out(trtp_rtcp_session_t* self, const trtp_rtp_packet_t* packet_rtp, tsk_size_t size) +{ + int ret = 0; + + if(!self || !packet_rtp || !packet_rtp->header){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(!self->is_started){ + TSK_DEBUG_ERROR("Not started"); + return -2; + } + + tsk_safeobj_lock(self); + + // create local source if not already done + // first destroy it if the ssrc don't match + if(self->source_local && self->source_local->ssrc != packet_rtp->header->ssrc){ + tsk_bool_t removed = tsk_false; + // local ssrc has changed: sould never happen ...but who know? + // remove the source + TSK_DEBUG_WARN("Not expected to be called"); + _trtp_rtcp_session_remove_source(self, self->source_local->ssrc, &removed); + TSK_OBJECT_SAFE_FREE(self->source_local); + TSK_OBJECT_SAFE_FREE(self->sdes); + self->packets_count = 0; + self->octets_count = 0; + if(removed){ + --self->senders; + --self->members; + } + } + if(!self->source_local){ + if(!(self->source_local = _trtp_rtcp_source_create(packet_rtp->header->ssrc, packet_rtp->header->seq_num, packet_rtp->header->timestamp))){ + TSK_DEBUG_ERROR("Failed to create new local source"); + } + // add the source (refresh the number of senders, ...) + _trtp_rtcp_session_add_source(self, self->source_local); + // 'members' and 'senders' were already initialized in the constructor + } + + if(!self->we_sent){ + self->we_sent = tsk_true; + } + + ++self->packets_count; + self->octets_count += (uint32_t)size; + + tsk_safeobj_unlock(self); + + return ret; +} + +int trtp_rtcp_session_process_rtp_in(trtp_rtcp_session_t* self, const trtp_rtp_packet_t* packet_rtp, tsk_size_t size) +{ + int ret = 0; + trtp_rtcp_source_t* source; + + if(!self || !packet_rtp || !packet_rtp->header){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(!self->is_started){ + TSK_DEBUG_INFO("RTCP session not started"); + return -2; + } + + tsk_safeobj_lock(self); + OnReceive(self, (const packet_)packet_rtp, EVENT_RTP, size); + if((source = _trtp_rtcp_session_find_source(self, packet_rtp->header->ssrc))){ + if(_trtp_rtcp_source_update_seq(source, packet_rtp->header->seq_num, packet_rtp->header->timestamp)){ + // RFC 3550 A.8 Estimating the Interarrival Jitter + /* uint32_t expected = (source->cycles + source->max_seq) - source->base_seq + 1; */ + double arrival = (((double)(source->max_ts - source->base_ts) / (double)source->rate) * 1000); + int32_t transit = (int32_t)arrival - packet_rtp->header->timestamp; + int32_t d = (transit - source->transit); + if(d < 0) d = -d; + source->transit = transit; + source->jitter += (1./16.) * ((double)d - source->jitter); + } + TSK_OBJECT_SAFE_FREE(source); + } + + tsk_safeobj_unlock(self); + + return ret; +} + +int trtp_rtcp_session_process_rtcp_in(trtp_rtcp_session_t* self, const void* buffer, tsk_size_t size) +{ + int ret = 0; + trtp_rtcp_packet_t* packet_rtcp = tsk_null; + + if(!self || !buffer || size < TRTP_RTCP_HEADER_SIZE){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(!self->is_started){ + TSK_DEBUG_ERROR("Not started"); + return -2; + } + + // derialize the RTCP packet for processing + packet_rtcp = trtp_rtcp_packet_deserialize(buffer, size); + if(packet_rtcp){ + tsk_safeobj_lock(self); + OnReceive(self, + (const packet_)packet_rtcp, + (packet_rtcp->header->type == trtp_rtcp_packet_type_bye) ? EVENT_BYE : EVENT_REPORT, + size); + if(packet_rtcp->header->type == trtp_rtcp_packet_type_sr){ + trtp_rtcp_source_t* source; + const trtp_rtcp_report_sr_t* sr = (const trtp_rtcp_report_sr_t*)packet_rtcp; + if((source = _trtp_rtcp_session_find_source(self, sr->ssrc))){ + source->ntp_lsw = sr->sender_info.ntp_lsw; + source->ntp_msw = sr->sender_info.ntp_msw; + source->dlsr = tsk_time_now(); + TSK_OBJECT_SAFE_FREE(source); + } + } + tsk_safeobj_unlock(self); // must be before callback() + + if(self->callback){ + ret = self->callback(self->callback_data, packet_rtcp); + } + TSK_OBJECT_SAFE_FREE(packet_rtcp); + } + + + return ret; +} + +int trtp_rtcp_session_signal_pkt_loss(trtp_rtcp_session_t* self, uint32_t ssrc_media, const uint16_t* seq_nums, tsk_size_t count) +{ + trtp_rtcp_report_rr_t* rr; + if(!self || !self->source_local || !seq_nums || !count){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if(!self->is_started){ + TSK_DEBUG_ERROR("Not started"); + return -1; + } + + tsk_safeobj_lock(self); + + if((rr = trtp_rtcp_report_rr_create_2(self->source_local->ssrc))){ + trtp_rtcp_report_rtpfb_t* rtpfb; + if((rtpfb = trtp_rtcp_report_rtpfb_create_nack(self->source_local->ssrc, ssrc_media, seq_nums, count))){ + trtp_rtcp_packet_add_packet((trtp_rtcp_packet_t*)rr, (trtp_rtcp_packet_t*)rtpfb, tsk_false); + _trtp_rtcp_session_send_pkt(self, (trtp_rtcp_packet_t*)rr); + TSK_OBJECT_SAFE_FREE(rtpfb); + } + TSK_OBJECT_SAFE_FREE(rr); + } + + tsk_safeobj_unlock(self); + + return 0; +} + +// Frame corrupted means the prediction chain is broken -> Send FIR +int trtp_rtcp_session_signal_frame_corrupted(trtp_rtcp_session_t* self, uint32_t ssrc_media) +{ + trtp_rtcp_report_rr_t* rr; + if(!self || !self->source_local){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if(!self->is_started){ + TSK_DEBUG_ERROR("Not started"); + return -1; + } + + tsk_safeobj_lock(self); + + if((rr = trtp_rtcp_report_rr_create_2(self->source_local->ssrc))){ + trtp_rtcp_report_psfb_t* psfb_fir = trtp_rtcp_report_psfb_create_fir(self->fir_seqnr++, self->source_local->ssrc, ssrc_media); + if(psfb_fir){ + trtp_rtcp_packet_add_packet((trtp_rtcp_packet_t*)rr, (trtp_rtcp_packet_t*)psfb_fir, tsk_false); + _trtp_rtcp_session_send_pkt(self, (trtp_rtcp_packet_t*)rr); + TSK_OBJECT_SAFE_FREE(psfb_fir); + } + TSK_OBJECT_SAFE_FREE(rr); + } + + tsk_safeobj_unlock(self); + return 0; +} + +// for now send just a FIR +int trtp_rtcp_session_signal_jb_error(struct trtp_rtcp_session_s* self, uint32_t ssrc_media) +{ + return trtp_rtcp_session_signal_frame_corrupted(self, ssrc_media); +} + +tnet_fd_t trtp_rtcp_session_get_local_fd(const struct trtp_rtcp_session_s* self) +{ + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return TNET_INVALID_FD; + } + return self->local_fd; +} + +int trtp_rtcp_session_set_local_fd(struct trtp_rtcp_session_s* self, tnet_fd_t local_fd) +{ + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->local_fd = local_fd; + return 0; +} + +int trtp_rtcp_session_set_net_transport(struct trtp_rtcp_session_s* self, struct tnet_transport_s* transport) +{ + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + TSK_OBJECT_SAFE_FREE(self->transport); + self->transport = tsk_object_ref(transport); + return 0; +} + +static tsk_bool_t _trtp_rtcp_session_have_source(trtp_rtcp_session_t* self, uint32_t ssrc) +{ + tsk_list_item_t* item; + tsk_list_foreach(item, self->sources){ + if(TRTP_RTCP_SOURCE(item->data)->ssrc == ssrc){ + return tsk_true; + } + } + return tsk_false; +} + +// find source by ssrc +// the caller must release the returned object +static trtp_rtcp_source_t* _trtp_rtcp_session_find_source(trtp_rtcp_session_t* self, uint32_t ssrc) +{ + tsk_list_item_t* item; + tsk_list_foreach(item, self->sources){ + if(TRTP_RTCP_SOURCE(item->data)->ssrc == ssrc){ + return tsk_object_ref(item->data); + } + } + return tsk_null; +} + +// find or add source by ssrc +// the caller must release the returned object +static trtp_rtcp_source_t* _trtp_rtcp_session_find_or_add_source(trtp_rtcp_session_t* self, uint32_t ssrc, uint16_t seq_if_add, uint32_t ts_id_add) +{ + trtp_rtcp_source_t* source; + if((source = _trtp_rtcp_session_find_source(self, ssrc))){ + return source; + } + + if((source = _trtp_rtcp_source_create(ssrc, seq_if_add, ts_id_add))){ + if((_trtp_rtcp_session_add_source(self, source)) != 0){ + TSK_DEBUG_ERROR("Failed to add source"); + TSK_OBJECT_SAFE_FREE(source); + return tsk_null; + } + return tsk_object_ref(source); + } + return tsk_null; +} + +int _trtp_rtcp_session_add_source(trtp_rtcp_session_t* self, trtp_rtcp_source_t* source) +{ + if(!self || !source){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tsk_list_lock(self->sources); + source = tsk_object_ref(source); + tsk_list_push_back_data(self->sources, (void**)&source); + tsk_list_unlock(self->sources); + + return 0; +} + +// adds a source if doesn't exist +static int _trtp_rtcp_session_add_source_2(trtp_rtcp_session_t* self, uint32_t ssrc, uint16_t seq, uint32_t ts, tsk_bool_t *added) +{ + int ret = 0; + tsk_list_item_t* item; + trtp_rtcp_source_t* source; + + tsk_list_lock(self->sources); + tsk_list_foreach(item, self->sources){ + if(TRTP_RTCP_SOURCE(item->data)->ssrc == ssrc){ + tsk_list_unlock(self->sources); + *added = tsk_false; + return 0; + } + } + + tsk_list_unlock(self->sources); + + if((source = _trtp_rtcp_source_create(ssrc, seq, ts))){ + ret = _trtp_rtcp_session_add_source(self, source); + } + + TSK_OBJECT_SAFE_FREE(source); + + *added = tsk_true; + return ret; +} + +int _trtp_rtcp_session_remove_source(trtp_rtcp_session_t* self, uint32_t ssrc, tsk_bool_t *removed) +{ + *removed = tsk_false; + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + tsk_list_lock(self->sources); + if((*removed = tsk_list_remove_item_by_pred(self->sources, __pred_find_source_by_ssrc, &ssrc)) == tsk_true){ + // ... + } + tsk_list_unlock(self->sources); + return 0; +} + +static tsk_size_t _trtp_rtcp_session_send_pkt(trtp_rtcp_session_t* self, trtp_rtcp_packet_t* pkt) +{ + tsk_size_t ret = 0; + tsk_size_t __num_bytes_pad = 0; + tsk_buffer_t* buffer; + + if(!self->remote_addr || self->local_fd <= 0){ + TSK_DEBUG_ERROR("Invalid network settings"); + return 0; + } + +#if HAVE_SRTP + if(self->srtp.session) __num_bytes_pad = (SRTP_MAX_TRAILER_LEN + 0x4); +#endif + + // SDES + if(!self->sdes && (self->sdes = trtp_rtcp_report_sdes_create_null())){ + trtp_rtcp_sdes_chunck_t* chunck = trtp_rtcp_sdes_chunck_create(self->source_local->ssrc); + if(chunck){ + static const char* _name = "test@doubango.org"; + trtp_rtcp_sdes_chunck_add_item(chunck, trtp_rtcp_sdes_item_type_cname, self->cname, (uint8_t)tsk_strlen(self->cname)); + trtp_rtcp_sdes_chunck_add_item(chunck, trtp_rtcp_sdes_item_type_name, _name, (uint8_t)tsk_strlen(_name)); + trtp_rtcp_report_sdes_add_chunck(self->sdes, chunck); + TSK_OBJECT_SAFE_FREE(chunck); + } + } + if(self->sdes){ + trtp_rtcp_packet_add_packet(pkt, (trtp_rtcp_packet_t*)self->sdes, tsk_true); + } + + if((buffer = trtp_rtcp_packet_serialize(pkt, __num_bytes_pad))){ + void* data = buffer->data; + int size = (int)buffer->size; +#if HAVE_SRTP + if(self->srtp.session){ + if(srtp_protect_rtcp(((srtp_t)*self->srtp.session), data, &size) != err_status_ok){ + TSK_DEBUG_ERROR("srtp_protect_rtcp() failed"); + } + } +#endif + ret = _trtp_rtcp_session_send_raw(self, data, size); + TSK_OBJECT_SAFE_FREE(buffer); + } + + return ret; +} + +static tsk_size_t _trtp_rtcp_session_send_raw(trtp_rtcp_session_t* self, const void* data, tsk_size_t size) +{ + tsk_size_t ret = 0; + if (!self || !data || !size) { + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + if (self->is_ice_turn_active) { + ret = (tnet_ice_ctx_send_turn_rtcp(self->ice_ctx, data, size) == 0) ? size : 0; // returns #0 if ok + } + else { + ret = self->transport + ? tnet_transport_sendto(self->transport, self->local_fd, self->remote_addr, data, size) + : tnet_sockfd_sendto(self->local_fd, self->remote_addr, data, size); + } + return ret; +} + +static int _trtp_rtcp_session_timer_callback(const void* arg, tsk_timer_id_t timer_id) +{ + trtp_rtcp_session_t* session = (trtp_rtcp_session_t*)arg; + tsk_safeobj_lock(session); // must + if(session->timer.id_bye == timer_id){ + session->timer.id_bye = TSK_INVALID_TIMER_ID; + OnExpire(session, EVENT_BYE); + } + else if(session->timer.id_report == timer_id){ + session->timer.id_report = TSK_INVALID_TIMER_ID; + OnExpire(session, EVENT_REPORT); + } + tsk_safeobj_unlock(session); + return 0; +} + +static tsk_bool_t IsRtpPacket(const packet_ p) +{ + return (TSK_OBJECT_HEADER(p)->__def__ == trtp_rtp_packet_def_t); +} + +static PacketType_ PacketType(const packet_ p) +{ + if(IsRtpPacket(p)){ + return PACKET_RTP; + } + else{ + switch(((const trtp_rtcp_packet_t*)p)->header->type){ + case trtp_rtcp_packet_type_bye: return PACKET_BYE; + default: return PACKET_RTCP_REPORT; + } + } +} + +// Returns true if the packet is from a member or not +// also checks all csrc +static tsk_bool_t NewMember(trtp_rtcp_session_t* session, const packet_ p) +{ + uint32_t ssrc = 0; + if(IsRtpPacket(p)){ + const trtp_rtp_packet_t* packet_rtp = (const trtp_rtp_packet_t*)p; + tsk_size_t i; + for(i = 0; i < packet_rtp->header->csrc_count && i < sizeof(packet_rtp->header->csrc)/sizeof(packet_rtp->header->csrc[0]); ++i){ + if(!(_trtp_rtcp_session_have_source(session, packet_rtp->header->csrc[i]))){ + return tsk_false; + } + } + ssrc = packet_rtp->header->ssrc; + } + else{ + switch(((const trtp_rtcp_packet_t*)p)->header->type){ + case trtp_rtcp_packet_type_rr: ssrc = ((const trtp_rtcp_report_rr_t*)p)->ssrc; break; + case trtp_rtcp_packet_type_sr: ssrc = ((const trtp_rtcp_report_sr_t*)p)->ssrc; break; + case trtp_rtcp_packet_type_bye: + { + tsk_size_t i; + const trtp_rtcp_report_bye_t* bye = (const trtp_rtcp_report_bye_t*)p; + for(i = 0; i < TRTP_RTCP_PACKET(bye)->header->rc; ++i){ + if(!_trtp_rtcp_session_have_source(session, bye->ssrc_list[i])){ + return tsk_false; + } + } + return tsk_true; + } + default: return tsk_false; + } + } + + return !_trtp_rtcp_session_have_source(session, ssrc); +} + +#define NewSender(session, p) NewMember((session), (p)) + +static tsk_size_t AddMemberUsingRTCPPacket(trtp_rtcp_session_t* session, const trtp_rtcp_packet_t* p, tsk_bool_t sender) +{ + trtp_rtcp_packets_L_t* packets = tsk_null; + trtp_rtcp_rblocks_L_t* blocks = tsk_null; + tsk_bool_t added = tsk_false; + tsk_size_t count = 0; + + switch(p->header->type){ + case trtp_rtcp_packet_type_rr: + { + const trtp_rtcp_report_rr_t* rr = (const trtp_rtcp_report_rr_t*)p; + _trtp_rtcp_session_add_source_2(session, ((const trtp_rtcp_report_rr_t*)p)->ssrc, 0, 0, &added); + if(added) ++count; + + packets = rr->packets; + blocks = rr->blocks; + break; + } + case trtp_rtcp_packet_type_sr: + { + const trtp_rtcp_report_sr_t* sr = (const trtp_rtcp_report_sr_t*)p; + _trtp_rtcp_session_add_source_2(session, ((const trtp_rtcp_report_sr_t*)p)->ssrc, 0, 0, &added); + if(added) ++count; + packets = sr->packets; + blocks = sr->blocks; + break; + } + default: + { + break; + } + } + + if(!sender){ + if(packets){ + const tsk_list_item_t *item; + tsk_list_foreach(item, packets){ + AddMemberUsingRTCPPacket(session, (const trtp_rtcp_packet_t*)item->data, sender); + } + } + if(blocks){ + const tsk_list_item_t *item; + tsk_list_foreach(item, blocks){ + _trtp_rtcp_session_add_source_2(session, TRTP_RTCP_RBLOCK(item->data)->ssrc, 0, 0, &added); + if(added) ++count; + } + } + } + + return count; +} + +static tsk_size_t AddMember_(trtp_rtcp_session_t* session, const packet_ p, tsk_bool_t sender) +{ + tsk_size_t count = 0; + if(IsRtpPacket(p)){ + const trtp_rtp_packet_t* packet_rtp = (const trtp_rtp_packet_t*)p; + tsk_size_t i; + tsk_bool_t added = tsk_false; + _trtp_rtcp_session_add_source_2(session, packet_rtp->header->ssrc, packet_rtp->header->seq_num, packet_rtp->header->timestamp, &added); + if(added) ++count; + for(i = 0; i < packet_rtp->header->csrc_count && i < sizeof(packet_rtp->header->csrc)/sizeof(packet_rtp->header->csrc[0]); ++i){ + _trtp_rtcp_session_add_source_2(session, packet_rtp->header->csrc[i], 0, 0, &added); + if(added) ++count; + } + } + else{ + count += AddMemberUsingRTCPPacket(session, (const trtp_rtcp_packet_t*) p, sender); + } + return count; +} + +#define AddMember(session, p) AddMember_((session), (p), tsk_false) +#define AddSender(session, p) AddMember_((session), (p), tsk_true) + + +static tsk_size_t RemoveMemberUsingRTCPPacket(trtp_rtcp_session_t* session, const trtp_rtcp_packet_t* p) +{ + trtp_rtcp_packets_L_t* packets = tsk_null; + trtp_rtcp_rblocks_L_t* blocks = tsk_null; + tsk_bool_t removed = tsk_false; + tsk_size_t count = 0; + + switch(p->header->type){ + case trtp_rtcp_packet_type_rr: + { + const trtp_rtcp_report_rr_t* rr = (const trtp_rtcp_report_rr_t*)p; + _trtp_rtcp_session_remove_source(session, ((const trtp_rtcp_report_rr_t*)p)->ssrc, &removed); + if(removed) ++count; + + packets = rr->packets; + blocks = rr->blocks; + break; + } + case trtp_rtcp_packet_type_sr: + { + const trtp_rtcp_report_sr_t* sr = (const trtp_rtcp_report_sr_t*)p; + _trtp_rtcp_session_remove_source(session, ((const trtp_rtcp_report_sr_t*)p)->ssrc, &removed); + if(removed) ++count; + packets = sr->packets; + blocks = sr->blocks; + break; + } + default: + { + break; + } + } + + if(packets){ + const tsk_list_item_t *item; + tsk_list_foreach(item, packets){ + RemoveMemberUsingRTCPPacket(session, (const trtp_rtcp_packet_t*)item->data); + } + } + if(blocks){ + const tsk_list_item_t *item; + tsk_list_foreach(item, blocks){ + _trtp_rtcp_session_remove_source(session, TRTP_RTCP_RBLOCK(item->data)->ssrc, &removed); + if(removed) ++count; + } + } + + + return count; +} + +static tsk_size_t RemoveMember(trtp_rtcp_session_t* session, const packet_ p) +{ + tsk_size_t count = 0; + if(IsRtpPacket(p)){ + const trtp_rtp_packet_t* packet_rtp = (const trtp_rtp_packet_t*)p; + tsk_size_t i; + tsk_bool_t removed = tsk_false; + _trtp_rtcp_session_remove_source(session, packet_rtp->header->ssrc, &removed); + if(removed) ++count; + for(i = 0; i < packet_rtp->header->csrc_count && i < sizeof(packet_rtp->header->csrc)/sizeof(packet_rtp->header->csrc[0]); ++i){ + _trtp_rtcp_session_remove_source(session, packet_rtp->header->csrc[i], &removed); + if(removed) ++count; + } + } + else{ + count += RemoveMemberUsingRTCPPacket(session, (const trtp_rtcp_packet_t*) p); + } + return count; +} + +#define RemoveSender(session, p) RemoveMember((session), (p)) + +// Sends BYE in synchronous mode +static void SendBYEPacket(trtp_rtcp_session_t* session, event_ e) +{ + trtp_rtcp_report_bye_t* bye; + tsk_size_t __num_bytes_pad = 0; + + if(!session->remote_addr || session->local_fd <= 0){ + TSK_DEBUG_ERROR("Invalid network settings"); + return; + } + + tsk_safeobj_lock(session); + +#if HAVE_SRTP + if(session->srtp.session) __num_bytes_pad = (SRTP_MAX_TRAILER_LEN + 0x4); +#endif + + if(session->source_local && (bye = trtp_rtcp_report_bye_create_2(session->source_local->ssrc))){ + tsk_buffer_t* buffer; + // serialize and send the packet + if((buffer = trtp_rtcp_packet_serialize((const trtp_rtcp_packet_t*)bye, __num_bytes_pad))){ + void* data = buffer->data; + int size = (int)buffer->size; +#if HAVE_SRTP + if(session->srtp.session){ + if(srtp_protect_rtcp(((srtp_t)*session->srtp.session), data, &size) != err_status_ok){ + TSK_DEBUG_ERROR("srtp_protect_rtcp() failed"); + } + } +#endif + _trtp_rtcp_session_send_raw(session, data, size); + TSK_OBJECT_SAFE_FREE(buffer); + } + TSK_OBJECT_SAFE_FREE(bye); + } + + tsk_safeobj_unlock(session); +} + +// returns sent packet size +static tsk_size_t SendRTCPReport(trtp_rtcp_session_t* session, event_ e) +{ + tsk_size_t ret = 0; + + tsk_safeobj_lock(session); + + if(session->initial){ + // Send Receiver report (manadatory to be the first on) + trtp_rtcp_report_rr_t* rr = trtp_rtcp_report_rr_create_2(session->source_local->ssrc); + if(rr){ + // serialize and send the packet + ret = _trtp_rtcp_session_send_pkt(session, (trtp_rtcp_packet_t*)rr); + TSK_OBJECT_SAFE_FREE(rr); + } + } + else{ + trtp_rtcp_report_sr_t* sr = trtp_rtcp_report_sr_create_null(); + uint32_t media_ssrc_list[16] = {0}; + uint32_t media_ssrc_list_count = 0; + if(sr){ + uint64_t ntp_now = tsk_time_ntp(); + uint64_t time_now = tsk_time_now(); + trtp_rtcp_rblock_t* rblock; + trtp_rtcp_source_t* source; + tsk_list_item_t *item; + tsk_bool_t packet_lost = tsk_false; + + // sender info + sr->ssrc = session->source_local->ssrc; + sr->sender_info.ntp_msw = (ntp_now >> 32); + sr->sender_info.ntp_lsw = (ntp_now & 0xFFFFFFFF); + sr->sender_info.sender_pcount = session->packets_count; + sr->sender_info.sender_ocount = session->octets_count; + { /* rtp_timestamp */ + struct timeval tv; + uint64_t rtp_timestamp = (time_now - session->time_start) * (session->source_local->rate / 1000); + tv.tv_sec = (long)(rtp_timestamp / 1000); + tv.tv_usec = (long)(rtp_timestamp - ((rtp_timestamp / 1000) * 1000)) * 1000; +#if 1 + sr->sender_info.rtp_timestamp = (uint32_t)tsk_time_get_ms(&tv); +#else + sr->sender_info.rtp_timestamp = (uint32_t)tsk_time_get_ntp_ms(&tv); +#endif + } + + // report blocks + tsk_list_foreach(item, session->sources){ + if(!(source = (trtp_rtcp_source_t*)item->data) || !_trtp_rtcp_source_is_probed(source)){ + continue; + } + if((rblock = trtp_rtcp_rblock_create_null())){ + uint32_t expected, expected_interval, received_interval, lost_interval; + + rblock->ssrc = source->ssrc; + // RFC 3550 - A.3 Determining Number of Packets Expected and Lost + expected = (source->cycles + source->max_seq) - source->base_seq + 1; + expected_interval = expected - source->expected_prior; + source->expected_prior = expected; + received_interval = source->received - source->received_prior; + source->received_prior = source->received; + lost_interval = expected_interval - received_interval; + if (expected_interval == 0 || lost_interval <= 0) rblock->fraction = 0; + else rblock->fraction = (lost_interval << 8) / expected_interval; + rblock->cumulative_no_lost = ((expected - source->received)); + if(!packet_lost && rblock->fraction) packet_lost = tsk_true; + + rblock->last_seq = ((source->cycles & 0xFFFF) << 16) | source->max_seq; + rblock->jitter = (uint32_t)source->jitter; + rblock->lsr = ((source->ntp_msw & 0xFFFF) << 16) | ((source->ntp_lsw & 0xFFFF0000) >> 16); + if(source->dlsr){ + rblock->dlsr = (uint32_t)(((time_now - source->dlsr) * 65536) / 1000); // in units of 1/65536 seconds + } + + trtp_rtcp_report_sr_add_block(sr, rblock); + TSK_OBJECT_SAFE_FREE(rblock); + } + + if((media_ssrc_list_count + 1) < sizeof(media_ssrc_list)/sizeof(media_ssrc_list[0])){ + media_ssrc_list[media_ssrc_list_count++] = source->ssrc; + } + } + + if(media_ssrc_list_count > 0){ + // draft-alvestrand-rmcat-remb-02 + if(session->app_bw_max_download > 0 && session->app_bw_max_download != INT_MAX){ // INT_MAX or <=0 means undefined + // app_bw_max_download unit is kbps while create_afb_remb() expect bps + trtp_rtcp_report_psfb_t* psfb_afb_remb = trtp_rtcp_report_psfb_create_afb_remb(session->source_local->ssrc/*sender SSRC*/, media_ssrc_list, media_ssrc_list_count, (session->app_bw_max_download * 1024)); + if(psfb_afb_remb){ + TSK_DEBUG_INFO("Packing RTCP-AFB-REMB (bw_dwn=%d kbps) for outgoing RTCP-SR", session->app_bw_max_download); + trtp_rtcp_packet_add_packet((trtp_rtcp_packet_t*)sr, (trtp_rtcp_packet_t*)psfb_afb_remb, tsk_false); + TSK_OBJECT_SAFE_FREE(psfb_afb_remb); + } + } + } + + // serialize and send the packet + ret = _trtp_rtcp_session_send_pkt(session, (trtp_rtcp_packet_t*)sr); + TSK_OBJECT_SAFE_FREE(sr); + } + } + + tsk_safeobj_unlock(session); + return ret; +} + +static void Schedule(trtp_rtcp_session_t* session, double tn, event_ e) +{ + tsk_safeobj_lock(session); // must + switch(e){ + case EVENT_BYE: + if(!TSK_TIMER_ID_IS_VALID(session->timer.id_bye)){ + session->timer.id_bye = tsk_timer_manager_schedule(session->timer.handle_global, (uint64_t)tn, _trtp_rtcp_session_timer_callback, session); + } + break; + case EVENT_REPORT: + if(!TSK_TIMER_ID_IS_VALID(session->timer.id_report)){ + session->timer.id_report = tsk_timer_manager_schedule(session->timer.handle_global, (uint64_t)tn, _trtp_rtcp_session_timer_callback, session); + } + break; + default: TSK_DEBUG_ERROR("Unexpected code called"); break; + } + tsk_safeobj_unlock(session); +} + +#define Reschedule(session, tn, e) Schedule((session), (tn), (e)) + +static double rtcp_interval(int32_t members, + int32_t senders, + double rtcp_bw, + int32_t we_sent, + double avg_rtcp_size, + tsk_bool_t initial) +{ + /* + * Minimum average time between RTCP packets from this site (in + * seconds). This time prevents the reports from `clumping' when + * sessions are small and the law of large numbers isn't helping + * to smooth out the traffic. It also keeps the report interval + * from becoming ridiculously small during transient outages like + * a network partition. + */ + #define RTCP_MIN_TIME 5. + /* + * Fraction of the RTCP bandwidth to be shared among active + * senders. (This fraction was chosen so that in a typical + * session with one or two active senders, the computed report + * time would be roughly equal to the minimum report time so that + * we don't unnecessarily slow down receiver reports.) The + * receiver fraction must be 1 - the sender fraction. + */ + #define RTCP_SENDER_BW_FRACTION 0.25 + #define RTCP_RCVR_BW_FRACTION (1 - RTCP_SENDER_BW_FRACTION) + /* + * To compensate for "timer reconsideration" converging to a + * value below the intended average. + */ + #define COMPENSATION (2.71828 - 1.5) + + double t; /* interval */ + double rtcp_min_time = RTCP_MIN_TIME; + int n; /* no. of members for computation */ + + /* + * Very first call at application start-up uses half the min + * delay for quicker notification while still allowing some time + * before reporting for randomization and to learn about other + * sources so the report interval will converge to the correct + * interval more quickly. + */ + if (initial) { + rtcp_min_time /= 2; + } + /* + * Dedicate a fraction of the RTCP bandwidth to senders unless + * the number of senders is large enough that their share is + * more than that fraction. + */ + n = members; + if (senders <= members * RTCP_SENDER_BW_FRACTION) { + if (we_sent) { + rtcp_bw *= RTCP_SENDER_BW_FRACTION; + n = senders; + } else { + rtcp_bw *= RTCP_RCVR_BW_FRACTION; + n -= senders; + } + } + + /* + * The effective number of sites times the average packet size is + * the total number of octets sent when each site sends a report. + * Dividing this by the effective bandwidth gives the time + * interval over which those packets must be sent in order to + * meet the bandwidth target, with a minimum enforced. In that + * time interval we send one report so this time is also our + * average time between reports. + */ + t = avg_rtcp_size * n / rtcp_bw; + if (t < rtcp_min_time) t = rtcp_min_time; + + /* + * To avoid traffic bursts from unintended synchronization with + * other sites, we then pick our actual next report interval as a + * random number uniformly distributed between 0.5*t and 1.5*t. + */ + t = t * (drand48() + 0.5); + t = t / COMPENSATION; + + return (t * 1000); +} + + +static void OnExpire(trtp_rtcp_session_t* session, event_ e) +{ + /* This function is responsible for deciding whether to send an + * RTCP report or BYE packet now, or to reschedule transmission. + * It is also responsible for updating the pmembers, initial, tp, + * and avg_rtcp_size state variables. This function should be + * called upon expiration of the event timer used by Schedule(). + */ + + double t; /* Interval */ + double tn; /* Next transmit time */ + double tc; + + /* In the case of a BYE, we use "timer reconsideration" to + * reschedule the transmission of the BYE if necessary */ + + if (TypeOfEvent(e) == EVENT_BYE) { + t = rtcp_interval(session->members, + session->senders, + session->rtcp_bw, + session->we_sent, + session->avg_rtcp_size, + session->initial); + tn = session->tp + t; + if (tn <= session->tc()) { + SendBYEPacket(session, e); +#if 0 + exit(1); +#endif + } else { +#if 0 + Schedule(session, tn, e); +#else + Schedule(session, 0, e); +#endif + } + + } else if (TypeOfEvent(e) == EVENT_REPORT) { + t = rtcp_interval(session->members, + session->senders, + session->rtcp_bw, + session->we_sent, + session->avg_rtcp_size, + session->initial); + tn = session->tp + t; + if (tn <= (tc = session->tc())) { + tsk_size_t SentPacketSize = SendRTCPReport(session, e); + session->avg_rtcp_size = (1./16.)*SentPacketSize + (15./16.)*(session->avg_rtcp_size); + session->tp = tc; + + /* We must redraw the interval. Don't reuse the + one computed above, since its not actually + distributed the same, as we are conditioned + on it being small enough to cause a packet to + be sent */ + + t = rtcp_interval(session->members, + session->senders, + session->rtcp_bw, + session->we_sent, + session->avg_rtcp_size, + session->initial); +#if 0 + Schedule(session, t+tc, e); +#else + Schedule(session, t, e); +#endif + session->initial = tsk_false; + } else { +#if 0 + Schedule(session, tn, e); +#else + Schedule(session, 0, e); +#endif + } + session->pmembers = session->members; + } +} + +static void OnReceive(trtp_rtcp_session_t* session, const packet_ p, event_ e, tsk_size_t ReceivedPacketSize) +{ + /* What we do depends on whether we have left the group, and are + * waiting to send a BYE (TypeOfEvent(e) == EVENT_BYE) or an RTCP + * report. p represents the packet that was just received. */ + + if (PacketType(p) == PACKET_RTCP_REPORT) { + if (NewMember(session, p) && (TypeOfEvent(e) == EVENT_REPORT)) { + session->members += (int32_t)AddMember(session, p); + } + session->avg_rtcp_size = (1./16.)*ReceivedPacketSize + (15./16.)*(session->avg_rtcp_size); + } else if (PacketType(p) == PACKET_RTP) { +#if 0 + if (NewMember(session, p) && (TypeOfEvent(e) == EVENT_REPORT)) { + session->members += AddMember(session, p); + } + if (NewSender(session, p) && (TypeOfEvent(e) == EVENT_REPORT)) { + tsk_size_t count = AddSender(session, p); + session->senders += count; + session->members += count; + } +#else + if (NewSender(session, p)) { + tsk_size_t count = AddSender(session, p); + session->senders += (int32_t)count; + session->members += (int32_t)count; + } +#endif + } else if (PacketType(p) == PACKET_BYE) { + session->avg_rtcp_size = (1./16.)*ReceivedPacketSize + (15./16.)*(session->avg_rtcp_size); + + if (TypeOfEvent(e) == EVENT_REPORT) { + double tc = session->tc(); + tsk_size_t count = RemoveMember(session, p); + session->senders -= (int32_t)count; + session->members -= (int32_t)count; +#if 0 + if (NewSender(session, p) == tsk_false) { + RemoveSender(p); + session->senders -= 1; + } + if (NewMember(session, p) == tsk_false) { + RemoveMember(p); + session->members -= 1; + } +#endif + + if (session->members < session->pmembers && session->pmembers) { + session->tn = (time_tp)(tc + + (((double) session->members)/(session->pmembers))*(session->tn - tc)); + session->tp = (time_tp)(tc - + (((double) session->members)/(session->pmembers))*(tc - session->tp)); + + /* Reschedule the next report for time tn */ + + Reschedule(session, session->tn, e); + session->pmembers = session->members; + } + + } else if (TypeOfEvent(e) == EVENT_BYE) { + session->members += 1; + } + } +} diff --git a/tinyRTP/src/rtp/trtp_rtp_header.c b/tinyRTP/src/rtp/trtp_rtp_header.c new file mode 100644 index 0000000..e833b10 --- /dev/null +++ b/tinyRTP/src/rtp/trtp_rtp_header.c @@ -0,0 +1,259 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp_rtp_header.c + * @brief RTP header. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + + */ +#include "tinyrtp/rtp/trtp_rtp_header.h" + +#include "tnet_endianness.h" + +#include "tsk_memory.h" +#include "tsk_debug.h" + + /* RFC 3550 section 5.1 - RTP Fixed Header Fields + 0 1 2 3 + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P|X| CC |M| PT | sequence number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | synchronization source (SSRC) identifier | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | contributing source (CSRC) identifiers | + | .... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* Create new RTP header */ +trtp_rtp_header_t* trtp_rtp_header_create_null() +{ + return tsk_object_new(trtp_rtp_header_def_t); +} + +trtp_rtp_header_t* trtp_rtp_header_create(uint32_t ssrc, uint16_t seq_num, uint32_t timestamp, uint8_t payload_type, tsk_bool_t marker) +{ + trtp_rtp_header_t* header; + if((header = trtp_rtp_header_create_null())){ + header->version = TRTP_RTP_VERSION; + header->marker = marker ? 1 : 0; + header->payload_type = payload_type; + header->seq_num = seq_num; + header->timestamp = timestamp; + header->ssrc = ssrc; + } + return header; +} + +/* guess what is the minimum required size to serialize the header */ +tsk_size_t trtp_rtp_header_guess_serialbuff_size(const trtp_rtp_header_t *self) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + return (TRTP_RTP_HEADER_MIN_SIZE + (self->csrc_count << 2)); +} + +/* serialize the RTP header to a buffer */ +// the buffer size must be at least equal to "trtp_rtp_header_guess_serialbuff_size()" +// returns the number of written bytes +tsk_size_t trtp_rtp_header_serialize_to(const trtp_rtp_header_t *self, void *buffer, tsk_size_t size) +{ + tsk_size_t ret; + tsk_size_t i, j; + uint8_t* pbuff = (uint8_t*)buffer; + + if(!buffer || (size < (ret = trtp_rtp_header_guess_serialbuff_size(self)))){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + + // Octet-0: version(2), Padding(1), Extension(1), CSRC Count(4) + pbuff[0] = (((uint8_t)self->version)<< 6) | + (((uint8_t)self->padding)<< 5) | + (((uint8_t)self->extension)<< 4) | + ((uint8_t)self->csrc_count); + // Octet-1: Marker(1), Payload Type(7) + pbuff[1] = (((uint8_t)self->marker)<< 7) | + ((uint8_t)self->payload_type); + // Octet-2-3: Sequence number (16) + // *((uint16_t*)&pbuff[2]) = tnet_htons(self->seq_num); + pbuff[2] = self->seq_num >> 8; + pbuff[3] = self->seq_num & 0xFF; + // Octet-4-5-6-7: timestamp (32) + // ((uint32_t*)&pbuff[4]) = tnet_htonl(self->timestamp); + pbuff[4] = self->timestamp >> 24; + pbuff[5] = (self->timestamp >> 16) & 0xFF; + pbuff[6] = (self->timestamp >> 8) & 0xFF; + pbuff[7] = self->timestamp & 0xFF; + // Octet-8-9-10-11: SSRC (32) + //((uint32_t*)&pbuff[8]) = tnet_htonl(self->ssrc); + pbuff[8] = self->ssrc >> 24; + pbuff[9] = (self->ssrc >> 16) & 0xFF; + pbuff[10] = (self->ssrc >> 8) & 0xFF; + pbuff[11] = self->ssrc & 0xFF; + + // Octet-12-13-14-15-****: CSRC + for(i = 0, j = 12; i<self->csrc_count; ++i, ++j){ + // *((uint32_t*)&pbuff[12+i]) = tnet_htonl(self->csrc[i]); + pbuff[j] = self->csrc[i] >> 24; + pbuff[j + 1] = (self->csrc[i] >> 16) & 0xFF; + pbuff[j + 2] = (self->csrc[i] >> 8) & 0xFF; + pbuff[j + 3] = self->csrc[i] & 0xFF; + } + + return ret; +} + +/** Serialize rtp header object into binary buffer */ +tsk_buffer_t* trtp_rtp_header_serialize(const trtp_rtp_header_t *self) +{ + tsk_buffer_t* buffer; + tsk_size_t size; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + size = trtp_rtp_header_guess_serialbuff_size(self); + if(!(buffer = tsk_buffer_create(tsk_null, size))){ + TSK_DEBUG_ERROR("Failed to create new buffer"); + TSK_OBJECT_SAFE_FREE(buffer); + } + else{ + size = trtp_rtp_header_serialize_to(self, buffer->data, buffer->size); + } + + return buffer; +} + +/** Deserialize rtp header object from binary buffer */ +trtp_rtp_header_t* trtp_rtp_header_deserialize(const void *data, tsk_size_t size) +{ + trtp_rtp_header_t* header = tsk_null; + const uint8_t* pdata = (const uint8_t*)data; + uint8_t csrc_count, i; + + if(!data){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + if(size <TRTP_RTP_HEADER_MIN_SIZE){ + TSK_DEBUG_ERROR("Too short to contain RTP header"); + return tsk_null; + } + + /* Before starting to deserialize, get the "csrc_count" and check the length validity + * CSRC count (4 last bits) + */ + csrc_count = (*pdata & 0x0F); + if(size <(tsk_size_t)TRTP_RTP_HEADER_MIN_SIZE + (csrc_count << 2)){ + TSK_DEBUG_ERROR("Too short to contain RTP header"); + return tsk_null; + } + + if(!(header = trtp_rtp_header_create_null())){ + TSK_DEBUG_ERROR("Failed to create new RTP header"); + return tsk_null; + } + + /* version (2bits) */ + header->version = (*pdata >> 6); + /* Padding (1bit) */ + header->padding = ((*pdata >>5) & 0x01); + /* Extension (1bit) */ + header->extension = ((*pdata >>4) & 0x01); + /* CSRC Count (4bits) */ + header->csrc_count = csrc_count; + // skip octet + ++pdata; + + /* Marker (1bit) */ + header->marker = (*pdata >> 7); + /* Payload Type (7bits) */ + header->payload_type = (*pdata & 0x7F); + // skip octet + ++pdata; + + /* Sequence Number (16bits) */ + header->seq_num = pdata[0] << 8 | pdata[1]; + // skip octets + pdata += 2; + + /* timestamp (32bits) */ + header->timestamp = pdata[0] << 24 | pdata[1] << 16 | pdata[2] << 8 | pdata[3]; + // skip octets + pdata += 4; + + /* synchronization source (SSRC) identifier (32bits) */ + header->ssrc = pdata[0] << 24 | pdata[1] << 16 | pdata[2] << 8 | pdata[3]; + // skip octets + pdata += 4; + + /* contributing source (CSRC) identifiers */ + for(i=0; i<csrc_count; i++, pdata += 4){ + header->csrc[i] = pdata[0] << 24 | pdata[1] << 16 | pdata[2] << 8 | pdata[3]; + } + + return header; +} + + + + + + +//================================================================================================= +// RTP header object definition +// +static tsk_object_t* trtp_rtp_header_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtp_header_t *header = self; + if(header){ + } + return self; +} + +static tsk_object_t* trtp_rtp_header_dtor(tsk_object_t * self) +{ + trtp_rtp_header_t *header = self; + if(header){ + } + + return self; +} + +static const tsk_object_def_t trtp_rtp_header_def_s = +{ + sizeof(trtp_rtp_header_t), + trtp_rtp_header_ctor, + trtp_rtp_header_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_rtp_header_def_t = &trtp_rtp_header_def_s; diff --git a/tinyRTP/src/rtp/trtp_rtp_packet.c b/tinyRTP/src/rtp/trtp_rtp_packet.c new file mode 100644 index 0000000..dc5c7ea --- /dev/null +++ b/tinyRTP/src/rtp/trtp_rtp_packet.c @@ -0,0 +1,260 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp_rtp_packet.c + * @brief RTP packet. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + + */ +#include "tinyrtp/rtp/trtp_rtp_packet.h" + +#include "tnet_endianness.h" + +#include "tsk_memory.h" +#include "tsk_debug.h" + +#include <string.h> /* memcpy() */ + +/** Create new RTP packet */ +trtp_rtp_packet_t* trtp_rtp_packet_create_null() +{ + return tsk_object_new(trtp_rtp_packet_def_t); +} + +trtp_rtp_packet_t* trtp_rtp_packet_create(uint32_t ssrc, uint16_t seq_num, uint32_t timestamp, uint8_t payload_type, tsk_bool_t marker) +{ + trtp_rtp_packet_t* packet; + if((packet = tsk_object_new(trtp_rtp_packet_def_t))){ + packet->header = trtp_rtp_header_create(ssrc, seq_num, timestamp, payload_type, marker); + } + return packet; +} + +trtp_rtp_packet_t* trtp_rtp_packet_create_2(const trtp_rtp_header_t* header) +{ + trtp_rtp_packet_t* packet; + + if(!header){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + if((packet = tsk_object_new(trtp_rtp_packet_def_t))){ + packet->header = tsk_object_ref(TSK_OBJECT(header)); + } + return packet; +} + +/* guess what is the minimum required size to serialize the packet */ +tsk_size_t trtp_rtp_packet_guess_serialbuff_size(const trtp_rtp_packet_t *self) +{ + tsk_size_t size = 0; + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + size += trtp_rtp_header_guess_serialbuff_size(self->header); + if(self->extension.data && self->extension.size && self->header->extension){ + size += self->extension.size; + } + size += self->payload.size; + return size; +} + +/* serialize the RTP packet to a buffer */ +// the buffer size must be at least equal to "trtp_rtp_packet_guess_serialbuff_size()" +// returns the number of written bytes +tsk_size_t trtp_rtp_packet_serialize_to(const trtp_rtp_packet_t *self, void* buffer, tsk_size_t size) +{ + tsk_size_t ret; + tsk_size_t s; + uint8_t* pbuff = (uint8_t*)buffer; + + if(!buffer || (size < (ret = trtp_rtp_packet_guess_serialbuff_size(self)))){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + + s = trtp_rtp_header_serialize_to(self->header, pbuff, size); + pbuff += s; + + /* extension */ + if(self->extension.data && self->extension.size && self->header->extension){ + memcpy(pbuff, self->extension.data, self->extension.size); + pbuff += self->extension.size; + } + /* append payload */ + memcpy(pbuff, self->payload.data_const ? self->payload.data_const : self->payload.data, self->payload.size); + + return ret; +} + +/** Serialize rtp packet object into binary buffer */ +// num_bytes_pad: number of bytes to add to the buffer. Useful to have the packet byte aligned or to prepare for SRTP protection +// the padding bytes will not be added to the final buffer size +tsk_buffer_t* trtp_rtp_packet_serialize(const trtp_rtp_packet_t *self, tsk_size_t num_bytes_pad) +{ + tsk_buffer_t* buffer = tsk_null; + tsk_size_t size; + + if(!self || !self->header){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + size = (trtp_rtp_packet_guess_serialbuff_size(self) + num_bytes_pad); + if(size & 0x03) size += (4 - (size & 0x03)); + + if(!(buffer = tsk_buffer_create(tsk_null, size))){ + TSK_DEBUG_ERROR("Failed to create buffer with size = %u", (unsigned)size); + return tsk_null; + } + // shorten the buffer to hide the padding + buffer->size = trtp_rtp_packet_serialize_to(self, buffer->data, buffer->size); + return buffer; +} + +/** Deserialize rtp packet object from binary buffer */ +trtp_rtp_packet_t* trtp_rtp_packet_deserialize(const void *data, tsk_size_t size) +{ + trtp_rtp_packet_t* packet = tsk_null; + trtp_rtp_header_t *header; + tsk_size_t payload_size; + const uint8_t* pdata = data; + + if(!data){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + if(size< TRTP_RTP_HEADER_MIN_SIZE){ + TSK_DEBUG_ERROR("Too short to contain RTP message"); + return tsk_null; + } + + /* deserialize the RTP header (the packet itsel will be deserialized only if the header deserialization succeed) */ + if(!(header = trtp_rtp_header_deserialize(data, size))){ + TSK_DEBUG_ERROR("Failed to deserialize RTP header"); + return tsk_null; + } + else{ + /* create the packet */ + if(!(packet = trtp_rtp_packet_create_null())){ + TSK_DEBUG_ERROR("Failed to create new RTP packet"); + TSK_OBJECT_SAFE_FREE(header); + return tsk_null; + } + /* set the header */ + packet->header = header, + header = tsk_null; + + /* do not need to check overflow (have been done by trtp_rtp_header_deserialize()) */ + payload_size = (size - TRTP_RTP_HEADER_MIN_SIZE - (packet->header->csrc_count << 2)); + pdata = ((const uint8_t*)data) + (size - payload_size); + + /* RFC 3550 - 5.3.1 RTP Header Extension + If the X bit in the RTP header is one, a variable-length header + extension MUST be appended to the RTP header, following the CSRC list + if present. The header extension contains a 16-bit length field that + counts the number of 32-bit words in the extension, excluding the + four-octet extension header (therefore zero is a valid length). Only + a single extension can be appended to the RTP data header. + 0 1 2 3 + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | defined by profile | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | header extension | + | .... | + */ + if(packet->header->extension && payload_size>=4 /* extension min-size */){ + packet->extension.size = 4 /* first two 16-bit fields */ + (tnet_ntohs(*((uint16_t*)&pdata[2])) << 2/*words(32-bit)*/); + if((packet->extension.data = tsk_calloc(packet->extension.size, sizeof(uint8_t)))){ + memcpy(packet->extension.data, pdata, packet->extension.size); + } + payload_size -= packet->extension.size; + } + + packet->payload.size = payload_size; + if(payload_size && (packet->payload.data = tsk_calloc(packet->payload.size, sizeof(uint8_t)))){ + memcpy(packet->payload.data, (pdata + packet->extension.size), packet->payload.size); + } + else{ + TSK_DEBUG_ERROR("Failed to allocate new buffer"); + packet->payload.size = 0; + } + } + + return packet; +} + + + + + + + + + +//================================================================================================= +// RTP packet object definition +// +static tsk_object_t* trtp_rtp_packet_ctor(tsk_object_t * self, va_list * app) +{ + trtp_rtp_packet_t *packet = self; + if(packet){ + } + return self; +} +static tsk_object_t* trtp_rtp_packet_dtor(tsk_object_t * self) +{ + trtp_rtp_packet_t *packet = self; + if(packet){ + TSK_OBJECT_SAFE_FREE(packet->header); + TSK_FREE(packet->payload.data); + TSK_FREE(packet->extension.data); + packet->payload.data_const = tsk_null; + } + + return self; +} +// comparison must be by sequence number because of the jb +static int trtp_rtp_packet_cmp(const tsk_object_t *_p1, const tsk_object_t *_p2) +{ + const trtp_rtp_packet_t *p1 = _p1; + const trtp_rtp_packet_t *p2 = _p2; + + if(p1 && p1->header && p2 && p2->header){ + return (int)(p1->header->seq_num - p2->header->seq_num); + } + else if(!p1 && !p2) return 0; + else return -1; +} + +static const tsk_object_def_t trtp_rtp_packet_def_s = +{ + sizeof(trtp_rtp_packet_t), + trtp_rtp_packet_ctor, + trtp_rtp_packet_dtor, + trtp_rtp_packet_cmp, +}; +const tsk_object_def_t *trtp_rtp_packet_def_t = &trtp_rtp_packet_def_s; diff --git a/tinyRTP/src/rtp/trtp_rtp_session.c b/tinyRTP/src/rtp/trtp_rtp_session.c new file mode 100644 index 0000000..3ebfe03 --- /dev/null +++ b/tinyRTP/src/rtp/trtp_rtp_session.c @@ -0,0 +1,29 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp_rtp_session.c + * @brief RTP session. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + + */ +#include "tinyrtp/rtp/trtp_rtp_session.h"
\ No newline at end of file diff --git a/tinyRTP/src/trtp.c b/tinyRTP/src/trtp.c new file mode 100644 index 0000000..a0150d9 --- /dev/null +++ b/tinyRTP/src/trtp.c @@ -0,0 +1,30 @@ +/* +* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org> +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp.h + * @brief tinyRTP (Real-time Transport Protocol) as per RFC 3550. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + + */ +#include "tinyrtp/trtp.h" + diff --git a/tinyRTP/src/trtp_manager.c b/tinyRTP/src/trtp_manager.c new file mode 100644 index 0000000..6e6b00e --- /dev/null +++ b/tinyRTP/src/trtp_manager.c @@ -0,0 +1,1960 @@ +/* +* Copyright (C) 2012 Mamadou Diop +* Copyright (C) 2012-2013 Doubango Telecom <http://www.doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +/**@file trtp_manager.c +* @brief RTP/RTCP manager. +* +*/ +#include "tinyrtp/trtp_manager.h" + +#include "tinyrtp/rtp/trtp_rtp_packet.h" +#include "tinyrtp/rtcp/trtp_rtcp_packet.h" +#include "tinyrtp/rtcp/trtp_rtcp_session.h" + +#include "tnet_proxydetect.h" +#include "turn/tnet_turn_session.h" +#include "ice/tnet_ice_candidate.h" + +#include "tsk_string.h" +#include "tsk_memory.h" +#include "tsk_base64.h" +#include "tsk_md5.h" +#include "tsk_debug.h" + +#include <limits.h> /* INT_MAX */ + +#if !defined(TRTP_TRANSPORT_NAME) +# define TRTP_TRANSPORT_NAME "RTP/RTCP Manager" +#endif + +#if !defined(TRTP_DISABLE_SOCKETS_BEFORE_START) +# define TRTP_DISABLE_SOCKETS_BEFORE_START 0 +#endif +#if !defined(TRTP_TINY_RCVBUF) +# define TRTP_TINY_RCVBUF (256>>1/*Will be doubled and min on linux is 256*/) /* tiny buffer used to disable receiving */ +#endif + +#if !defined(TRTP_DSCP_RTP_DEFAULT) +# define TRTP_DSCP_RTP_DEFAULT /* 0x2e */ 0x00 +#endif + +#if !defined(TRTP_PORT_RANGE_START) +# define TRTP_PORT_RANGE_START 1024 +#endif +#if !defined(TRTP_PORT_RANGE_STOP) +# define TRTP_PORT_RANGE_STOP 65535 +#endif + +#if !defined(TRTP_DTLS_HANDSHAKING_TIMEOUT) +# define TRTP_DTLS_HANDSHAKING_TIMEOUT 1000 +#endif +#if !defined(TRTP_DTLS_HANDSHAKING_TIMEOUT_MAX) +# define TRTP_DTLS_HANDSHAKING_TIMEOUT_MAX (TRTP_DTLS_HANDSHAKING_TIMEOUT << 20) +#endif + +static const tmedia_srtp_type_t __srtp_types[] = { tmedia_srtp_type_sdes, tmedia_srtp_type_dtls }; + +static int _trtp_manager_recv_data(const trtp_manager_t* self, const uint8_t* data_ptr, tsk_size_t data_size, tnet_fd_t local_fd, const struct sockaddr_storage* remote_addr); +#define _trtp_manager_is_rtcpmux_active(self) ( (self) && ( (self)->use_rtcpmux && (!(self)->rtcp.local_socket || ((self)->transport && (self)->transport->master && (self)->transport->master->fd == (self)->rtcp.local_socket->fd)) ) ) +static int _trtp_manager_send_turn_dtls(struct tnet_ice_ctx_s* ice_ctx, const void* handshaking_data_ptr, tsk_size_t handshaking_data_size, tsk_bool_t use_rtcp_channel); +#define _trtp_manager_send_turn_dtls_rtp(ice_ctx, handshaking_data_ptr, handshaking_data_size) _trtp_manager_send_turn_dtls((ice_ctx), (handshaking_data_ptr), (handshaking_data_size), /*use_rtcp_channel =*/tsk_false) +#define _trtp_manager_send_turn_dtls_rtcp(ice_ctx, handshaking_data_ptr, handshaking_data_size) _trtp_manager_send_turn_dtls((ice_ctx), (handshaking_data_ptr), (handshaking_data_size), /*use_rtcp_channel =*/tsk_true) +#if HAVE_SRTP +static int _trtp_manager_srtp_set_enabled(trtp_manager_t* self, tmedia_srtp_type_t srtp_type, struct tnet_socket_s** sockets, tsk_size_t count, tsk_bool_t enabled); +static int _trtp_manager_srtp_activate(trtp_manager_t* self, tmedia_srtp_type_t srtp_type); +static int _trtp_manager_srtp_start(trtp_manager_t* self, tmedia_srtp_type_t srtp_type); +#endif /* HAVE_SRTP */ + + +/* ======================= Transport callback ========================== */ +static int _trtp_transport_layer_cb(const tnet_transport_event_t* e) +{ + trtp_manager_t* manager = (trtp_manager_t*)e->callback_data; + + switch(e->type){ + case event_data: + { + return _trtp_manager_recv_data(manager, e->data, e->size, e->local_fd, &e->remote_addr); + } + case event_brokenpipe: + { + tsk_safeobj_lock(manager); + tnet_fd_t broken_fd = e->local_fd; + tnet_socket_t* socket = tsk_null; + tsk_bool_t is_rtcp_socket = tsk_false; + + if (manager->transport && manager->transport->master && manager->transport->master->fd == broken_fd) { + socket = manager->transport->master; + } + else if (manager->rtcp.local_socket && manager->rtcp.local_socket->fd == broken_fd) { + socket = manager->rtcp.local_socket; + is_rtcp_socket = tsk_true; + } + if (socket) { + tsk_bool_t registered_fd = !!tnet_transport_have_socket(manager->transport, broken_fd); + if (registered_fd) { + tnet_transport_remove_socket(manager->transport, &broken_fd); // broken_fd=-1 + broken_fd = e->local_fd; // restore + } + if (tnet_socket_handle_brokenpipe(socket) == 0) { + if (registered_fd) { + tnet_transport_add_socket(manager->transport, socket->fd, socket->type, tsk_false/* do not take ownership */, tsk_true/* only Meaningful for tls*/, tsk_null); + } + if (manager->rtcp.session && trtp_rtcp_session_get_local_fd(manager->rtcp.session) == broken_fd) { + trtp_rtcp_session_set_local_fd(manager->rtcp.session, socket->fd); + } + } + } + tsk_safeobj_unlock(manager); + return 0; + } +#if HAVE_SRTP + /* DTLS - SRTP events */ + case event_dtls_handshake_succeed: + { + const tnet_socket_t* socket = manager->transport && manager->transport->master && (manager->transport->master->fd == e->local_fd) + ? manager->transport->master + : ((manager->rtcp.local_socket && manager->rtcp.local_socket->fd == e->local_fd) ? manager->rtcp.local_socket : tsk_null); + if(!socket){ + TSK_DEBUG_ERROR("DTLS data from unknown socket"); + break; + } + + if (!manager->dtls.srtp_handshake_succeed) { + manager->dtls.srtp_handshake_succeed = (socket == manager->transport->master); + } + if (!manager->dtls.srtcp_handshake_succeed) { + manager->dtls.srtcp_handshake_succeed = (socket == manager->rtcp.local_socket) || _trtp_manager_is_rtcpmux_active(manager); + } + + TSK_DEBUG_INFO("dtls.srtp_handshake_succeed=%d, dtls.srtcp_handshake_succeed=%d", manager->dtls.srtp_handshake_succeed, manager->dtls.srtcp_handshake_succeed); + TSK_DEBUG_INFO("DTLS-DTLS-SRTP socket [%s]:%d handshake succeed", socket->ip, socket->port); + + if(manager->dtls.srtp_handshake_succeed && manager->dtls.srtcp_handshake_succeed){ + // alter listeners + if(manager->dtls.cb.fun){ + manager->dtls.cb.fun(manager->dtls.cb.usrdata, trtp_srtp_dtls_event_type_handshake_succeed, "DTLS handshake succeed"); + } + } + break; + } + case event_dtls_fingerprint_mismatch: + case event_dtls_handshake_failed: + case event_dtls_error: + { + // alter listeners + if(manager->dtls.cb.fun){ + const char* reason = (e->type == event_dtls_fingerprint_mismatch) + ? "DTLS-SRTP fingerprint mismatch" + : (e->type == event_dtls_handshake_failed ? "DTLS-SRTP handshake failed" : "DTLS error"); + manager->dtls.cb.fun(manager->dtls.cb.usrdata, trtp_srtp_dtls_event_type_handshake_failed, reason); + } + break; + } + case event_dtls_srtp_data: + { + /* KEY||SALT */ + /* rfc 5764 - 4.2. Key Derivation */ + tsk_bool_t is_rtp = (manager->transport->master && manager->transport->master->fd == e->local_fd); + tsk_bool_t is_rtcp = (manager->rtcp.local_socket && manager->rtcp.local_socket->fd == e->local_fd); + if(is_rtp || is_rtcp){ + unsigned int master_salt_length, master_key_length; + +#if HAVE_SRTP_PROFILE_GET_MASTER_KEY_LENGTH + master_key_length = srtp_profile_get_master_key_length(manager->dtls.crypto_selected == HMAC_SHA1_32 ? srtp_profile_aes128_cm_sha1_32 : srtp_profile_aes128_cm_sha1_80); +#else + master_key_length = (128 >> 3); // cipher_key_length - rfc5764 4.1.2. SRTP Protection Profiles +#endif +#if HAVE_SRTP_PROFILE_GET_MASTER_SALT_LENGTH + master_salt_length = srtp_profile_get_master_salt_length(manager->dtls.crypto_selected == HMAC_SHA1_32 ? srtp_profile_aes128_cm_sha1_32 : srtp_profile_aes128_cm_sha1_80); +#else + master_salt_length = (112 >> 3); // cipher_salt_length - rfc5764 4.1.2. SRTP Protection Profiles +#endif + if(((master_key_length + master_salt_length) << 1) > e->size){ + TSK_DEBUG_ERROR("%d not a valid size for this profile", (int)e->size); + } + else{ + int ret; + const uint8_t* data_ptr = e->data; + const uint8_t *lk, *ls, *rk, *rs; + if(manager->dtls.local.setup == tnet_dtls_setup_passive){ + rk = &data_ptr[0]; + lk = rk + master_key_length; + rs = (lk + master_key_length); + ls = (rs + master_salt_length); + } + else{ + lk = &data_ptr[0]; + rk = lk + master_key_length; + ls = (rk + master_key_length); + rs = (ls + master_salt_length); + } + // set key||salt + if((ret = trtp_srtp_set_key_and_salt_remote(manager, manager->dtls.crypto_selected, rk, master_key_length, rs, master_salt_length, is_rtp))){ + if(manager->dtls.cb.fun) manager->dtls.cb.fun(manager->dtls.cb.usrdata, trtp_srtp_dtls_event_type_fatal_error, "Failed to set remote DTSL-SRTP key||salt"); + return ret; + } + if((ret = trtp_srtp_set_key_and_salt_local(manager, manager->dtls.crypto_selected, lk, master_key_length, ls, master_salt_length, is_rtp))){ + if(manager->dtls.cb.fun) manager->dtls.cb.fun(manager->dtls.cb.usrdata, trtp_srtp_dtls_event_type_fatal_error, "Failed to set local DTSL-SRTP key||salt"); + return ret; + } + + if(is_rtp){ + manager->dtls.srtp_connected = manager->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][manager->dtls.crypto_selected].rtp.initialized; + if(_trtp_manager_is_rtcpmux_active(manager)){ + manager->dtls.srtcp_connected = tsk_true; + } + } + else{ // rtcp + manager->dtls.srtcp_connected = manager->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][manager->dtls.crypto_selected].rtcp.initialized; + } + TSK_DEBUG_INFO("dtls.srtp_connected=%d, dtls.srtcp_connected=%d", manager->dtls.srtp_connected, manager->dtls.srtcp_connected); + + if(manager->dtls.srtp_connected && manager->dtls.srtcp_connected){ + // start DTLS-SRTP + if((ret = _trtp_manager_srtp_start(manager, manager->srtp_type))){ + if(manager->dtls.cb.fun) manager->dtls.cb.fun(manager->dtls.cb.usrdata, trtp_srtp_dtls_event_type_fatal_error, "Failed to set start DTSL-SRTP engine"); + return ret; + } + + TSK_DEBUG_INFO("!!DTLS-SRTP started!!"); + + // alter listeners + if(manager->dtls.cb.fun){ + manager->dtls.cb.fun(manager->dtls.cb.usrdata, trtp_srtp_dtls_event_type_started, "DTLS started"); + } + } + } + } + + break; + } + case event_dtls_srtp_profile_selected: + { + if(manager->transport->master && manager->transport->master->fd == e->local_fd){ + /* Only (SRTP_AES128_CM_SHA1_80 | SRTP_AES128_CM_SHA1_32) because of _trtp_manager_srtp_activate() */ + TSK_DEBUG_INFO("event_dtls_srtp_profile_selected: %.*s", 22, (const char*)e->data); + manager->dtls.crypto_selected = HMAC_SHA1_80; + if(tsk_strnequals(e->data, "SRTP_AES128_CM_SHA1_32", 22)){ + manager->dtls.crypto_selected = HMAC_SHA1_32; + } + } + break; + } +#endif /* HAVE_SRTP */ + + case event_connected: + case event_closed: + { + + break; + } + default: + break; + } + return 0; +} + +static int _trtp_transport_dtls_handshaking_timer_cb(const void* arg, tsk_timer_id_t timer_id) +{ + int ret = 0; +#if HAVE_SRTP + trtp_manager_t* manager = (trtp_manager_t*)arg; + + tsk_safeobj_lock(manager); + if (manager->is_started && manager->dtls.timer_hanshaking.id == timer_id && manager->srtp_state == trtp_srtp_state_activated && manager->srtp_type == tmedia_srtp_type_dtls) { + // retry DTLS-SRTP handshaking if srtp-type is DTLS-SRTP and the engine is activated + struct tnet_socket_s* sockets[] = { manager->dtls.srtp_connected ? tsk_null : manager->transport->master , manager->dtls.srtcp_connected ? tsk_null : manager->rtcp.local_socket }; + const struct sockaddr_storage* remote_addrs[] = { &manager->rtp.remote_addr, &manager->rtcp.remote_addr }; + TSK_DEBUG_INFO("_trtp_transport_dtls_handshaking_timer_cb(timeout=%llu)", manager->dtls.timer_hanshaking.timeout); + tnet_transport_dtls_do_handshake(manager->transport, sockets, 2, remote_addrs, 2); + if (manager->is_ice_turn_active) { + // means TURN is active and handshaking data must be sent using this channel + const void* data[] = { tsk_null, tsk_null }; + tsk_size_t size[] = { 0, 0 }; + if ((ret = tnet_transport_dtls_get_handshakingdata(manager->transport, (const struct tnet_socket_s**)sockets, 2, data, size))) { + return ret; + } + if (data[0] && size[0]) { + ret = _trtp_manager_send_turn_dtls_rtp(manager->ice_ctx, data[0], size[0]); + } + if (data[1] && size[1]) { + ret = _trtp_manager_send_turn_dtls_rtcp(manager->ice_ctx, data[1], size[1]); + } + } + // increase timeout + manager->dtls.timer_hanshaking.timeout += (TRTP_DTLS_HANDSHAKING_TIMEOUT >> 1); + if ((manager->dtls.timer_hanshaking.timeout < TRTP_DTLS_HANDSHAKING_TIMEOUT_MAX) && !(manager->dtls.srtp_connected && manager->dtls.srtcp_connected)) { + manager->dtls.timer_hanshaking.id = tsk_timer_manager_schedule(manager->timer_mgr_global, manager->dtls.timer_hanshaking.timeout, _trtp_transport_dtls_handshaking_timer_cb, manager); + } + else { + manager->dtls.timer_hanshaking.id = TSK_INVALID_TIMER_ID; // invalidate timer id (not required but should be done by good citizen) + manager->dtls.timer_hanshaking.timeout = TRTP_DTLS_HANDSHAKING_TIMEOUT; // reset timeout + } + } + tsk_safeobj_unlock(manager); +#endif + + return ret; +} + +#if 0 +static int _trtp_manager_enable_sockets(trtp_manager_t* self) +{ + int rcv_buf = tmedia_defaults_get_rtpbuff_size(); + int snd_buf = tmedia_defaults_get_rtpbuff_size(); + int ret; + + if(!self->socket_disabled){ + return 0; + } + + if(!self || !self->transport){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if((ret = setsockopt(self->transport->master->fd, SOL_SOCKET, SO_RCVBUF, (char*)&rcv_buf, sizeof(rcv_buf)))){ + TNET_PRINT_LAST_ERROR("setsockopt(SOL_SOCKET, SO_RCVBUF) has failed with error code %d", ret); + return ret; + } + if((ret = setsockopt(self->transport->master->fd, SOL_SOCKET, SO_SNDBUF, (char*)&snd_buf, sizeof(snd_buf)))){ + TNET_PRINT_LAST_ERROR("setsockopt(SOL_SOCKET, SO_RCVBUF) has failed with error code %d", ret); + return ret; + } + + self->socket_disabled = tsk_false; + return 0; +} +#endif + +static trtp_manager_t* _trtp_manager_create(tsk_bool_t use_rtcp, const char* local_ip, tsk_bool_t ipv6, tmedia_srtp_type_t srtp_type, tmedia_srtp_mode_t srtp_mode) +{ + trtp_manager_t* manager; + +#if HAVE_SRTP + static tsk_bool_t __strp_initialized = tsk_false; + err_status_t srtp_err; + if(!__strp_initialized){ + if((srtp_err = srtp_init()) != err_status_ok){ + TSK_DEBUG_ERROR("srtp_init() failed with error code = %d", srtp_err); + } + __strp_initialized = (srtp_err == err_status_ok); + } +#endif + + if((manager = tsk_object_new(trtp_manager_def_t))){ + manager->use_rtcp = use_rtcp; + manager->local_ip = tsk_strdup(local_ip); + manager->use_ipv6 = ipv6; +#if HAVE_SRTP + manager->srtp_type = srtp_type; + manager->srtp_mode = srtp_mode; +#endif + manager->rtp.payload_type = 127; + } + return manager; +} + +static int _trtp_manager_recv_data(const trtp_manager_t* self, const uint8_t* data_ptr, tsk_size_t data_size, tnet_fd_t local_fd, const struct sockaddr_storage* remote_addr) +{ + tsk_bool_t is_rtp_rtcp, is_rtcp = tsk_false, is_rtp = tsk_false, is_stun, is_dtls; + + if (!self->is_started) { + TSK_DEBUG_INFO("RTP manager not started yet"); + return 0; + } + + // defined when RTCP-MUX is disabled and RTCP port is equal to "RTP Port + 1" + + // rfc5764 - 5.1.2. Reception + // rfc5761 - 4. Distinguishable RTP and RTCP Packets + + is_rtp_rtcp = (127 < *data_ptr && *data_ptr < 192); + if(is_rtp_rtcp){ + is_stun = is_dtls = tsk_false; + is_rtcp = (self->rtcp.local_socket && self->rtcp.local_socket->fd == local_fd); + if(!is_rtcp && data_size >= 2 && (data_ptr[1] & 0x80)){ + if(is_rtp_rtcp){ + switch((data_ptr[1] & 0x7F)){ + case 64: case 65: + case 72: case 73: case 74: case 75: case 76: + case 77: case 78: + case 79: is_rtcp = tsk_true; break; + } + } + } + is_rtp = !is_rtcp; + } + else{ + is_dtls = !is_rtp_rtcp && (19 < *data_ptr && *data_ptr < 64); + is_stun = !is_dtls && TNET_STUN_BUFF_IS_STUN2(data_ptr, data_size); /* MUST NOT USE: "(*data_ptr < 2)" beacause of "Old VAT" which starts with "0x00" */; + } + + if(is_dtls){ + tnet_socket_t* socket = (self->transport && self->transport->master && self->transport->master->fd == local_fd) + ? self->transport->master + : ((self->rtcp.local_socket && self->rtcp.local_socket->fd == local_fd) ? self->rtcp.local_socket : tsk_null); + if (socket && socket->dtlshandle) { + TSK_DEBUG_INFO("Receive %s-DTLS data on ip=%s and port=%d", (socket == self->transport->master) ? "RTP" : "RTCP", socket->ip, socket->port); + // Handle incoming data then do handshaking + tnet_dtls_socket_handle_incoming_data(socket->dtlshandle, data_ptr, data_size); + if (self->is_ice_turn_active) { + // means TURN is active and handshaking data must be sent using the channel + const void* data = tsk_null; + tsk_size_t size = 0; + if (tnet_transport_dtls_get_handshakingdata(self->transport, (const struct tnet_socket_s**)&socket, 1, &data, &size) == 0) { + if (data && size > 0){ + if (self->rtcp.local_socket == socket) { + /*ret = */_trtp_manager_send_turn_dtls_rtcp(self->ice_ctx, data, size); + } + else { + /*ret = */_trtp_manager_send_turn_dtls_rtp(self->ice_ctx, data, size); + } + } + } + } + } + return 0; + } + + if(is_stun){ + static tsk_bool_t role_conflict = tsk_false; + if(self->ice_ctx){ + return tnet_ice_ctx_recv_stun_message(self->ice_ctx, data_ptr, data_size, local_fd, remote_addr, &role_conflict); + } + return 0; + } + if(is_rtcp){ + if(!self->is_symetric_rtcp_checked && self->is_force_symetric_rtp){ + ((trtp_manager_t*)self)->is_symetric_rtcp_checked = tsk_true; + if(!self->is_ice_neg_ok && remote_addr){ // do not force symetric RTCP is ICE negotiation succeed + TSK_DEBUG_INFO("Using symetric RTCP for [%s]:%d", self->rtcp.remote_ip, self->rtcp.remote_port); + ((trtp_manager_t*)self)->rtcp.remote_addr = *remote_addr; + } + } + + if(self->rtcp.session){ + #if HAVE_SRTP + err_status_t status; + if(self->srtp_ctx_neg_remote){ + srtp_t session = self->srtp_ctx_neg_remote->rtcp.initialized ? self->srtp_ctx_neg_remote->rtcp.session : self->srtp_ctx_neg_remote->rtp.session; + if((status = srtp_unprotect_rtcp(session, (void*)data_ptr, (int*)&data_size)) != err_status_ok){ + if (status == err_status_replay_fail) { + // replay (because of RTCP-NACK nothing to worry about) + TSK_DEBUG_INFO("srtp_unprotect(RTCP) returned 'err_status_replay_fail'"); + return 0; + } + else { + TSK_DEBUG_ERROR("srtp_unprotect(RTCP) failed with error code=%d", (int)status); + return -1; + } + } + } + #endif + return trtp_rtcp_session_process_rtcp_in(self->rtcp.session, data_ptr, data_size); + } + TSK_DEBUG_WARN("No RTCP session"); + return 0; + } + if(is_rtp){ + if(!self->is_symetric_rtp_checked && self->is_force_symetric_rtp){ + ((trtp_manager_t*)self)->is_symetric_rtp_checked = tsk_true; + if(!self->is_ice_neg_ok && remote_addr){ // do not force symetric RTP is ICE negotiation succeed + TSK_DEBUG_INFO("Using symetric RTP for [%s]:%d", self->rtp.remote_ip, self->rtp.remote_port); + ((trtp_manager_t*)self)->rtp.remote_addr = *remote_addr; + } + } + + if(self->rtp.cb.fun){ + trtp_rtp_packet_t* packet_rtp = tsk_null; + #if HAVE_SRTP + err_status_t status; + if(self->srtp_ctx_neg_remote){ + if((status = srtp_unprotect(self->srtp_ctx_neg_remote->rtp.session, (void*)data_ptr, (int*)&data_size)) != err_status_ok){ + if (status == err_status_replay_fail) { + // replay (because of RTCP-NACK nothing to worry about) + TSK_DEBUG_INFO("srtp_unprotect(RTP) returned 'err_status_replay_fail'"); + return 0; + } + else { + TSK_DEBUG_ERROR("srtp_unprotect(RTP) failed with error code=%d, seq_num=%u", (int)status, (data_size > 4 ? tnet_ntohs_2(&data_ptr[2]) : 0x0000)); + return -1; + } + } + } + #endif + if((packet_rtp = trtp_rtp_packet_deserialize(data_ptr, data_size))){ + // update remote SSRC based on received RTP packet + ((trtp_manager_t*)self)->rtp.ssrc.remote = packet_rtp->header->ssrc; + // forward to the callback function (most likely "session_av") + self->rtp.cb.fun(self->rtp.cb.usrdata, packet_rtp); + // forward packet to the RTCP session + if(self->rtcp.session){ + trtp_rtcp_session_process_rtp_in(self->rtcp.session, packet_rtp, data_size); + } + TSK_OBJECT_SAFE_FREE(packet_rtp); + return 0; + } + else{ + TSK_DEBUG_ERROR("RTP packet === NOK"); + return -1; + } + } + return 0; + } + + TSK_DEBUG_INFO("Received unknown packet type"); + return 0; +} + +// Sends DTLS handshaking data record by record to avoid UDP IP fragmentation issues (each record length will be < Length(MTU)) +//!\ This is required even if the local transport is TCP/TLS because the relayed (TURN) transport could be UDP +static int _trtp_manager_send_turn_dtls(struct tnet_ice_ctx_s* ice_ctx, const void* handshaking_data_ptr, tsk_size_t handshaking_data_size, tsk_bool_t use_rtcp_channel) +{ + const uint8_t *record_ptr, *records_ptr = handshaking_data_ptr; + tsk_size_t record_size; + int records_len = (int)handshaking_data_size, ret = 0; + int(*_ice_ctx_send_turn_data)(struct tnet_ice_ctx_s* self, const void* data, tsk_size_t size) = use_rtcp_channel ? tnet_ice_ctx_send_turn_rtcp : tnet_ice_ctx_send_turn_rtp; + if (!ice_ctx || !handshaking_data_ptr || !handshaking_data_size) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + while (records_len > 0 && (ret = tnet_dtls_socket_get_record_first(records_ptr, (tsk_size_t)records_len, &record_ptr, &record_size)) == 0) { + ret = _ice_ctx_send_turn_data(ice_ctx, record_ptr, record_size); + + records_len -= (int)record_size; + records_ptr += record_size; + } + return ret; +} + +#if HAVE_SRTP +/* +Enables SDES-SRTP and DTLS-SRTP +Enabling SRTP will allow us to get "crypto" lines for negotiation +At this stage the sockets are not ready to send DTLS datagrams -> Good for ICE negotiation +If ICE is enabled DTLS-SRTP will not be enabled as the transport is "null" +*/ +static int _trtp_manager_srtp_set_enabled(trtp_manager_t* self, tmedia_srtp_type_t srtp_type, struct tnet_socket_s** sockets, tsk_size_t count, tsk_bool_t enabled) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if((self->srtp_mode == tmedia_srtp_mode_optional || self->srtp_mode == tmedia_srtp_mode_mandatory)){ + int ret; + if(enabled){ + if(srtp_type & tmedia_srtp_type_sdes){ + trtp_srtp_ctx_init( + &self->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_80], + 1, + HMAC_SHA1_80, + self->rtp.ssrc.local + ); + trtp_srtp_ctx_init( + &self->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_32], + 2, + HMAC_SHA1_32, + self->rtp.ssrc.local + ); + } + + if(srtp_type & tmedia_srtp_type_dtls){ + /* + Enables DTLS on the transport without activating it on the sockets + Enabling DTLS will allow us to get the certificate fingerprints for negotiation + At this stage the sockets are not ready to send DTLS datagrams -> Good for ICE negotiation + */ + if(self->transport){ + if((ret = tnet_transport_dtls_set_enabled(self->transport, enabled, tsk_null, 0))){ + return ret; + } + if((ret = trtp_manager_set_dtls_certs(self, self->dtls.file_ca, self->dtls.file_pbk, self->dtls.file_pvk, self->dtls.cert_verif))){ + return ret; + } + self->dtls.state = trtp_srtp_state_enabled; + } + else{ + self->dtls.enable_postponed = tsk_true; + } + } + self->srtp_state = trtp_srtp_state_enabled; + } + else { + if (srtp_type & tmedia_srtp_type_dtls) { + if (self->transport) { + ret = tnet_transport_dtls_set_enabled(self->transport, tsk_false, sockets, count); + } + self->dtls.state = trtp_srtp_state_none; + self->dtls.enable_postponed = tsk_false; + self->dtls.srtp_connected = self->dtls.srtp_handshake_succeed = tsk_false; + self->dtls.srtcp_connected = self->dtls.srtcp_handshake_succeed = tsk_false; + } + + // SRTP context is used by both DTLS and SDES -> only destroy them if requested to be disabled on both + if((~srtp_type & self->srtp_type) == tmedia_srtp_type_none){ + trtp_srtp_ctx_deinit(&self->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][0]); + trtp_srtp_ctx_deinit(&self->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][1]); + self->srtp_ctx_neg_local = tsk_null; + self->srtp_ctx_neg_remote = tsk_null; + self->srtp_state = trtp_srtp_state_none; + // Reset SRTP session to the RTCP session manager + if (self->rtcp.session) { + trtp_rtcp_session_set_srtp_sess(self->rtcp.session, tsk_null); + } + } + } + } + + return 0; +} + +static int _trtp_manager_srtp_activate(trtp_manager_t* self, tmedia_srtp_type_t srtp_type) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if(srtp_type != tmedia_srtp_type_none && (self->srtp_mode == tmedia_srtp_mode_optional || self->srtp_mode == tmedia_srtp_mode_mandatory)){ + int ret; + if(self->srtp_state < trtp_srtp_state_enabled){ + TSK_DEBUG_ERROR("SRTP engine not enabled yet"); + return -2; + } + if((srtp_type & tmedia_srtp_type_dtls) && (self->dtls.state >= trtp_srtp_state_enabled || self->dtls.enable_postponed)){ + /* + Activates DTLS on the transport and on both RTP and RTCP sockets + At this stage the sockets are ready to send/recv DTLS datagrams + */ + struct tnet_socket_s* sockets[] = { self->transport->master , self->rtcp.local_socket }; + const struct sockaddr_storage* remote_addrs[] = { &self->rtp.remote_addr, &self->rtcp.remote_addr }; + tsk_bool_t store_handshakingdata[] = { self->is_ice_turn_active, self->is_ice_turn_active }; + + // check if DTLS-SRTP enabling was postponed because the net transport was not ready (could happen if ICE is ON) + if(self->dtls.enable_postponed){ + if ((ret = _trtp_manager_srtp_set_enabled(self, self->srtp_type, sockets, sizeof(sockets) / sizeof(sockets[0]), tsk_true))) { + return ret; + } + self->dtls.enable_postponed = tsk_false; + } + + // activate "use_srtp" (rfc5764 section 4.1) on the transport + // this should be done before enabling DTLS sockets to be sure that newly created/enabled ones will use "use_srtp" extension + if((ret = tnet_transport_dtls_use_srtp(self->transport, "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32", sockets, 2))){ + return ret; + } + // enabling DTLS on the sockets will create the "dtlshandle" field and change the type from UDP to DTLS + if((ret = tnet_transport_dtls_set_enabled(self->transport, tsk_true, sockets, 2))){ + return ret; + } + + /* At this step the DTLS "dtlshandle" is created and the socket types changed from UDP to DTLS */ + + // pass the remote certificate fingerprint to both SRTP and SRTCP sockets + // the fingerprint will be verified if this option is enabled on the SSL context + // we'll be notified via the callback if there are fingerprint mismatch after the begining of the handshaking + if((ret = tnet_transport_dtls_set_remote_fingerprint(self->transport, &self->dtls.remote.fp, self->dtls.remote.fp_hash, sockets, 2))){ + return ret; + } + // setting the "setup" allow each DTLS socket to know if it's a client or server + // setup="active" means it's up to us to send the "DTLS client hello" message (otherwise "server hello" will be sent) + if((ret = tnet_transport_dtls_set_setup(self->transport, self->dtls.local.setup, sockets, 2))){ + return ret; + } + // whether to send DTLS handshaking data using the provided sockets or TURN session + if ((ret = tnet_transport_dtls_set_store_handshakingdata(self->transport, store_handshakingdata[0], sockets, 2))) { + return ret; + } + // start handshaking process (will do nothing if already completed) + if ((ret = tnet_transport_dtls_do_handshake(self->transport, sockets, 2, remote_addrs, 2))) { + return ret; + } + if (store_handshakingdata[0]) { + // means TURN is active and handshaking data must be sent using the channel + const void* data[] = { tsk_null, tsk_null }; + tsk_size_t size[] = { 0, 0 }; + if ((ret = tnet_transport_dtls_get_handshakingdata(self->transport, (const struct tnet_socket_s**)sockets, 2, data, size))) { + return ret; + } + if (data[0] && size[0]) { + ret = tnet_ice_ctx_send_turn_rtp(self->ice_ctx, data[0], size[0]); + } + if (data[1] && size[1]) { + ret = tnet_ice_ctx_send_turn_rtcp(self->ice_ctx, data[1], size[1]); + } + } + + self->dtls.state = trtp_srtp_state_activated; + } + + self->srtp_state = trtp_srtp_state_activated; + + // SDES-SRTP could be started right now while DTLS requires the handshaking to terminate + if (srtp_type & tmedia_srtp_type_sdes) { + return _trtp_manager_srtp_start(self, self->srtp_type); + } + } + return 0; +} + +static int _trtp_manager_srtp_start(trtp_manager_t* self, tmedia_srtp_type_t srtp_type) +{ + const trtp_srtp_ctx_xt *ctx_remote, *ctx_local; + tsk_bool_t use_different_keys; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(self->srtp_state < trtp_srtp_state_activated){ + TSK_DEBUG_ERROR("SRTP engine not activated yet"); + return -2; + } + + ctx_remote = self->srtp_contexts[TRTP_SRTP_LINE_IDX_REMOTE][HMAC_SHA1_80].rtp.initialized + ? &self->srtp_contexts[TRTP_SRTP_LINE_IDX_REMOTE][HMAC_SHA1_80] + : &self->srtp_contexts[TRTP_SRTP_LINE_IDX_REMOTE][HMAC_SHA1_32]; + + // dtls uses different keys for rtp and srtp which is not the case for sdes + use_different_keys = !_trtp_manager_is_rtcpmux_active(self) && ((srtp_type & tmedia_srtp_type_dtls) == tmedia_srtp_type_dtls); + TSK_DEBUG_INFO("srtp_use_different_keys=%s", use_different_keys ? "true" : "false"); + + if(!ctx_remote->rtp.initialized || (use_different_keys && !ctx_remote->rtcp.initialized)){ + TSK_DEBUG_ERROR("SRTP remote context not initialized: Not expected at this state"); + return -2; + } + + // use same crypto type on both sides + ctx_local = &self->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][(int32_t)ctx_remote->rtp.crypto_type]; + if(!ctx_local->rtp.initialized || (use_different_keys && !ctx_local->rtcp.initialized)){ + TSK_DEBUG_ERROR("SRTP local context not initialized: Not expected at this state"); + return -2; + } + + // update negotiated crypto contexts used to encrypt()/decrypt() SRTP data + self->srtp_ctx_neg_remote = ctx_remote; + self->srtp_ctx_neg_local = ctx_local; + + self->srtp_state = trtp_srtp_state_started; + if(self->dtls.state >= trtp_srtp_state_activated){ + // this means the DTLS-SRTP is the active type intead of SDES + self->dtls.state = trtp_srtp_state_started; + } + + // Pass SRTP session to the RTCP session manager + trtp_rtcp_session_set_srtp_sess( + self->rtcp.session, + self->srtp_ctx_neg_local ? (use_different_keys ? &self->srtp_ctx_neg_local->rtcp.session : &self->srtp_ctx_neg_local->rtp.session) : tsk_null + ); + + /* At this step we are able to encrypt()/decrypt() SRTP data */ + + return 0; +} + +#endif /* HAVE_SRTP */ + +static int _trtp_manager_ice_init(trtp_manager_t* self) +{ + int ret = 0; + const tnet_ice_candidate_t *candidate_offer, *candidate_answer_src, *candidate_answer_dest; + struct tnet_socket_s *rtp_socket = tsk_null; + + if (!self || !self->ice_ctx) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if (!self->transport) { + // get rtp nominated symetric candidates + ret = tnet_ice_ctx_get_nominated_symetric_candidates(self->ice_ctx, TNET_ICE_CANDIDATE_COMPID_RTP, + &candidate_offer, &candidate_answer_src, &candidate_answer_dest); + self->is_ice_neg_ok = (ret == 0 && candidate_offer && candidate_answer_src && candidate_answer_dest); + self->is_ice_turn_active = self->is_ice_neg_ok && tnet_ice_ctx_is_turn_rtp_active(self->ice_ctx); + if (!self->is_ice_neg_ok) { + // If this code is called this means that ICE negotiation has failed + // This is not really an error because it could happen if the remote peer is not an ICE agent or is an ICE-lite + // in this case, use the first offered candidate which is the best one and already used in the "c=" line + if (!(candidate_offer = tnet_ice_ctx_get_local_candidate_first(self->ice_ctx))) { + // Must never happen as we always have at least one local "host" candidate + TSK_DEBUG_ERROR("ICE context not ready"); + ret = -3; + goto bail; + } + } + + // Get RTP socket + if (self->is_ice_turn_active && candidate_offer->turn.ss) { + if ((ret = tnet_turn_session_get_socket_local(candidate_offer->turn.ss, &rtp_socket))) { + goto bail; + } + } + else { + rtp_socket = tsk_object_ref(candidate_offer->socket); + } + + // create transport + if (!(self->transport = tnet_transport_create_2(rtp_socket, TRTP_TRANSPORT_NAME))) { + TSK_DEBUG_ERROR("Failed to create transport(%s:%u)", rtp_socket->ip, rtp_socket->port); + ret = -4; + goto bail; + } + // set rtp local and remote IPs and ports + if (candidate_answer_dest) { // could be "null" if remote peer is ICE-lite + tsk_strupdate(&self->rtp.remote_ip, candidate_answer_dest->connection_addr); + self->rtp.remote_port = candidate_answer_dest->port; + tsk_strupdate(&self->rtp.public_ip, candidate_offer->connection_addr); + self->rtp.public_port = candidate_offer->port; + } + + // get rtp nominated symetric candidates + if (self->use_rtcp) { + ret = tnet_ice_ctx_get_nominated_symetric_candidates(self->ice_ctx, TNET_ICE_CANDIDATE_COMPID_RTCP, + &candidate_offer, &candidate_answer_src, &candidate_answer_dest); + if (ret == 0 && candidate_offer && candidate_answer_src && candidate_answer_dest) { + // set rtp local and remote IPs and ports + tsk_strupdate(&self->rtcp.remote_ip, candidate_answer_dest->connection_addr); + self->rtcp.remote_port = candidate_answer_dest->port; + tsk_strupdate(&self->rtcp.public_ip, candidate_offer->connection_addr); + self->rtcp.public_port = candidate_offer->port; + TSK_OBJECT_SAFE_FREE(self->rtcp.local_socket); + // Get RTCP socket + if (self->is_ice_turn_active && candidate_offer->turn.ss) { + ret = tnet_turn_session_get_socket_local(candidate_offer->turn.ss, &self->rtcp.local_socket); + if (ret) { + goto bail; + } + } + else { + self->rtcp.local_socket = tsk_object_ref(candidate_offer->socket); + } + } + } + } + + // set callback functions + ret = tnet_transport_set_callback(self->transport, _trtp_transport_layer_cb, self); // NetTransport -> RtpManager + ret = tnet_ice_ctx_rtp_callback(self->ice_ctx, (tnet_ice_rtp_callback_f)_trtp_manager_recv_data, self); // ICE -> RtpManager + +bail: + TSK_OBJECT_SAFE_FREE(rtp_socket); + return ret; +} + + +/** Create RTP/RTCP manager */ +trtp_manager_t* trtp_manager_create(tsk_bool_t use_rtcp, const char* local_ip, tsk_bool_t ipv6, tmedia_srtp_type_t srtp_type, tmedia_srtp_mode_t srtp_mode) +{ + trtp_manager_t* manager; + if((manager = _trtp_manager_create(use_rtcp, local_ip, ipv6, srtp_type, srtp_mode))){ + } + return manager; +} + +/** Create RTP/RTCP manager */ +trtp_manager_t* trtp_manager_create_2(struct tnet_ice_ctx_s* ice_ctx, tmedia_srtp_type_t srtp_type, tmedia_srtp_mode_t srtp_mode) +{ + trtp_manager_t* manager; + const char* local_ip; + tsk_bool_t use_ipv6, use_rtcp; + + if(!ice_ctx){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + local_ip = (use_ipv6 = tnet_ice_ctx_use_ipv6(ice_ctx)) ? "::1" : "127.0.0.1"; + use_rtcp = tnet_ice_ctx_use_rtcp(ice_ctx); + + if((manager = _trtp_manager_create(use_rtcp, local_ip, use_ipv6, srtp_type, srtp_mode))){ + manager->ice_ctx = tsk_object_ref(ice_ctx); + } + return manager; +} + +int trtp_manager_set_ice_ctx(trtp_manager_t* self, struct tnet_ice_ctx_s* ice_ctx) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid ICE context"); + return -1; + } + TSK_OBJECT_SAFE_FREE(self->ice_ctx); + self->ice_ctx = tsk_object_ref(ice_ctx); + return 0; +} + +/** Prepares the RTP/RTCP manager */ +int trtp_manager_prepare(trtp_manager_t* self) +{ + const char *rtp_local_ip = tsk_null, *rtcp_local_ip = tsk_null; + tnet_port_t rtp_local_port = 0, rtcp_local_port = 0; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if(self->transport){ + TSK_DEBUG_ERROR("RTP/RTCP manager already prepared"); + return -2; + } + + if(self->ice_ctx){ + TSK_DEBUG_INFO("ICE enabled on RTP manager"); + // Get Sockets when the transport is started + rtp_local_ip = rtcp_local_ip = self->use_ipv6 ? "::1" : "127.0.0.1"; + rtp_local_port = 2; // ICE default rtp port, do not use zero which is reserved to disabled medias + rtcp_local_port = 1; // ICE default rtcp port, do not use zero which is reserved to disabled medias + } + else{ + #define __retry_count_max 5 + #define __retry_count_max_minus1 (__retry_count_max - 1) + uint8_t retry_count = __retry_count_max; + tnet_socket_type_t socket_type = self->use_ipv6 ? tnet_socket_type_udp_ipv6 : tnet_socket_type_udp_ipv4; + + /* Creates local rtp and rtcp sockets */ + while(retry_count--){ + /* random number in the range 1024 to 65535 */ + static int counter = 0; +#if 0 + tnet_port_t local_port = 6060; +#else + // first check => try to use port from latest active session if exist + tnet_port_t local_port = (retry_count == __retry_count_max_minus1 && (self->port_range.start <= self->rtp.public_port && self->rtp.public_port <= self->port_range.stop)) + ? self->rtp.public_port + : (((rand() ^ ++counter) % (self->port_range.stop - self->port_range.start)) + self->port_range.start); +#endif + local_port = (local_port & 0xFFFE); /* turn to even number */ + + /* beacuse failure will cause errors in the log, print a message to alert that there is + * nothing to worry about */ + TSK_DEBUG_INFO("RTP/RTCP manager[Begin]: Trying to bind to random ports"); + + /* RTP */ + if(!(self->transport = tnet_transport_create(self->local_ip, local_port, socket_type, TRTP_TRANSPORT_NAME))){ + TSK_DEBUG_ERROR("Failed to create RTP/RTCP Transport"); + return -3; + } + + /* RTCP */ + if (self->use_rtcp) { + if(!(self->rtcp.local_socket = tnet_socket_create(self->local_ip, local_port+1, socket_type))){ + TSK_DEBUG_WARN("Failed to bind to %d", local_port+1); + TSK_OBJECT_SAFE_FREE(self->transport); + continue; + } + } + + TSK_DEBUG_INFO("RTP/RTCP manager[End]: Trying to bind to random ports"); + break; + }// end-of-while(retry_count) + + rtp_local_ip = self->transport->master->ip; + rtp_local_port = self->transport->master->port; + if(self->rtcp.local_socket){ + rtcp_local_ip = self->rtcp.local_socket->ip; + rtcp_local_port = self->rtcp.local_socket->port; + + } + }// end-of-else(!ice) + + tsk_strupdate(&self->rtp.public_ip, rtp_local_ip); + self->rtp.public_port = rtp_local_port; + + tsk_strupdate(&self->rtcp.public_ip, rtcp_local_ip); + self->rtcp.public_port = rtcp_local_port; + + if(self->transport){ + /* set callback function */ + tnet_transport_set_callback(self->transport, _trtp_transport_layer_cb, self); + /* Disable receiving until we start the transport (To avoid buffering) */ +#if TRTP_DISABLE_SOCKETS_BEFORE_START + if(!self->socket_disabled){ + int err, optval = TRTP_TINY_RCVBUF; + if((err = setsockopt(self->transport->master->fd, SOL_SOCKET, SO_RCVBUF, (char*)&optval, sizeof(optval)))){ + TNET_PRINT_LAST_ERROR("setsockopt(SOL_SOCKET, SO_RCVBUF) has failed with error code %d", err); + } + self->socket_disabled = (err == 0); + } +#endif + } + + /* SRTP */ +#if HAVE_SRTP + { + // enable SRTP to allow negotiation + if (self->srtp_type != tmedia_srtp_mode_none) { + struct tnet_socket_s* sockets[] = { self->transport ? self->transport->master : tsk_null, self->rtcp.local_socket }; + _trtp_manager_srtp_set_enabled(self, self->srtp_type, sockets, sizeof(sockets)/sizeof(sockets[0]), tsk_true); + } + } +#endif + + return 0; +} + +#if HAVE_SRTP + +int trtp_manager_set_dtls_certs(trtp_manager_t* self, const char* ca, const char* pbk, const char* pvk, tsk_bool_t verify) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + // always save certificates even if not DTLS-SRTP transport + tsk_strupdate(&self->dtls.file_ca, ca); + tsk_strupdate(&self->dtls.file_pbk, pbk); + tsk_strupdate(&self->dtls.file_pvk, pvk); + self->dtls.cert_verif = verify; + + if((self->srtp_type & tmedia_srtp_type_dtls) && (self->srtp_mode == tmedia_srtp_mode_optional || self->srtp_mode == tmedia_srtp_mode_mandatory)){ + if(self->transport && tnet_transport_dtls_is_enabled(self->transport)){ + return tnet_transport_dtls_srtp_set_certs(self->transport, self->dtls.file_ca, self->dtls.file_pbk, self->dtls.file_pvk, self->dtls.cert_verif); + } + } + else{ + TSK_DEBUG_ERROR("DTLS certificates setting ignored for non-DTLS-SRTP transport"); + return -2; + } + + return 0; +} + +int trtp_manager_set_dtls_callback(trtp_manager_t* self, const void* usrdata, trtp_srtp_dtls_cb_f fun) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->dtls.cb.usrdata = usrdata; + self->dtls.cb.fun = fun; + + return 0; +} + +int trtp_manager_set_dtls_remote_fingerprint(trtp_manager_t* self, const tnet_fingerprint_t* fp, const char* fp_hash) +{ + tnet_dtls_hash_type_t hash; + if(!self || !fp || !fp_hash){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + hash = tnet_dtls_get_hash_from_string(fp_hash); + if(hash != tnet_dtls_hash_type_sha1 && hash != tnet_dtls_hash_type_sha256){ + TSK_DEBUG_ERROR("%s not supported as fingerprint hash", fp_hash); + return -2; + } + self->dtls.remote.fp_hash = hash; + memcpy(self->dtls.remote.fp, &(*fp)[0], sizeof(tnet_fingerprint_t)); + return 0; +} + +enum tnet_dtls_hash_type_e trtp_manager_get_dtls_remote_fingerprint_hash(trtp_manager_t* self) +{ + return (self ? self->dtls.remote.fp_hash : tnet_dtls_hash_type_none); +} + +int trtp_manager_set_dtls_local_setup(trtp_manager_t* self, tnet_dtls_setup_t setup, tsk_bool_t connection_new) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->dtls.local.setup = setup; + self->dtls.local.connection_new = connection_new; + return 0; +} + +const char* trtp_manager_get_dtls_local_fingerprint(trtp_manager_t* self, enum tnet_dtls_hash_type_e hash) +{ + if(!self || (int32_t)hash < 0 || (int32_t)hash >= TNET_DTLS_HASH_TYPE_MAX){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + if(!self->transport && self->dtls.file_pbk){ + static tnet_fingerprint_t fingerprint[TNET_DTLS_HASH_TYPE_MAX]; + if(tnet_dtls_get_fingerprint(self->dtls.file_pbk, &fingerprint[hash], hash) == 0){ + return (const char*)fingerprint[hash]; + } + } + return tnet_transport_dtls_get_local_fingerprint(self->transport, hash); +} + +tsk_bool_t trtp_manager_is_dtls_enabled(trtp_manager_t* self) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_false; + } + return (self->dtls.state >= trtp_srtp_state_enabled); +} + +tsk_bool_t trtp_manager_is_dtls_activated(trtp_manager_t* self) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_false; + } + return (self->dtls.state >= trtp_srtp_state_activated); +} + +tsk_bool_t trtp_manager_is_dtls_started(trtp_manager_t* self) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_false; + } + return (self->dtls.state >= trtp_srtp_state_started); +} + +tsk_bool_t trtp_manager_is_srtp_activated(trtp_manager_t* self) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_false; + } + return (self->srtp_state >= trtp_srtp_state_activated); +} + +tsk_bool_t trtp_manager_is_srtp_started(trtp_manager_t* self) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_false; + } + return (self->srtp_state >= trtp_srtp_state_started); +} + +/** Sets SRTP type used by the remote party */ +int trtp_manager_set_srtp_type_remote(trtp_manager_t* self, tmedia_srtp_type_t srtp_type_remote) +{ + tsk_size_t i; + int ret; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + for(i = 0; i < sizeof(__srtp_types)/sizeof(__srtp_types[i]); ++i) { + if ((self->srtp_type & __srtp_types[i]) && !(srtp_type_remote & __srtp_types[i])) { + struct tnet_socket_s* sockets[] = { self->transport ? self->transport->master : tsk_null, self->rtcp.local_socket }; + if ((ret = _trtp_manager_srtp_set_enabled(self, __srtp_types[i], sockets, sizeof(sockets) / sizeof(sockets[0]), tsk_false))) { + return ret; + } + self->srtp_type &= ~__srtp_types[i]; + } + } + return 0; +} + +int trtp_manager_set_srtp_type_local(trtp_manager_t* self, enum tmedia_srtp_type_e srtp_type, enum tmedia_srtp_mode_e srtp_mode) +{ + if (!self) { + TSK_DEBUG_ERROR("Invalid ICE context"); + return -1; + } + if (srtp_mode == tmedia_srtp_mode_none || srtp_type == tmedia_srtp_type_none) { + struct tnet_socket_s* sockets[] = { self->transport ? self->transport->master : tsk_null, self->rtcp.local_socket }; + _trtp_manager_srtp_set_enabled(self, self->srtp_type, sockets, sizeof(sockets) / sizeof(sockets[0]), tsk_false); + self->srtp_type = srtp_type; + self->srtp_mode = srtp_mode; + return 0; + } + + self->srtp_mode = srtp_mode; + return trtp_manager_set_srtp_type_remote(self, srtp_type); +} + +#endif /* HAVE_SRTP */ + +/** Indicates whether the manager is already ready or not */ +tsk_bool_t trtp_manager_is_ready(trtp_manager_t* self) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_false; + } + return (self->transport != tsk_null); +} + +/** Sets NAT Traversal context */ +int trtp_manager_set_natt_ctx(trtp_manager_t* self, struct tnet_nat_ctx_s* natt_ctx) +{ + int ret; + + if(!self || !self->transport || !natt_ctx){ + if(self && self->ice_ctx){ + return 0; // Nat context is not needed when ICE is enabled + } + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if(!(ret = tnet_transport_set_natt_ctx(self->transport, natt_ctx))){ + tnet_ip_t public_ip = {0}; + tnet_port_t public_port = 0; + // get RTP public IP and Port + if(!tnet_transport_get_public_ip_n_port(self->transport, self->transport->master->fd, &public_ip, &public_port)){ + tsk_strupdate(&self->rtp.public_ip, public_ip); + self->rtp.public_port = public_port; + } + // get RTCP public IP and Port + memset(public_ip, 0, sizeof(public_ip)); + public_port = 0; + if(self->rtcp.local_socket && !tnet_transport_get_public_ip_n_port(self->transport, self->rtcp.local_socket->fd, &public_ip, &public_port)){ + tsk_strupdate(&self->rtcp.public_ip, public_ip); + self->rtcp.public_port = public_port; + } + // re-enable sockets to be able to receive STUN packets +#if 0 + _trtp_manager_enable_sockets(self); +#endif + } + return ret; +} + +/** Sets RTP callback */ +int trtp_manager_set_rtp_callback(trtp_manager_t* self, trtp_rtp_cb_f fun, const void* usrdata) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + self->rtp.cb.fun = fun; + self->rtp.cb.usrdata = usrdata; + + return 0; +} + +/** Sets RTCP callback */ +int trtp_manager_set_rtcp_callback(trtp_manager_t* self, trtp_rtcp_cb_f fun, const void* usrdata) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + self->rtcp.cb.fun = fun; + self->rtcp.cb.usrdata = usrdata; + if(self->rtcp.session){ + return trtp_rtcp_session_set_callback(self->rtcp.session, fun, usrdata); + } + + return 0; +} + +/** Sets the payload type */ +int trtp_manager_set_payload_type(trtp_manager_t* self, uint8_t payload_type) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->rtp.payload_type = payload_type; + return 0; +} + +int trtp_manager_set_rtp_dscp(trtp_manager_t* self, int32_t dscp) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->rtp.dscp = dscp; + return 0; +} + +/** Sets remote parameters for rtp session */ +int trtp_manager_set_rtp_remote(trtp_manager_t* self, const char* remote_ip, tnet_port_t remote_port) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + // if ICE is enabled then, these values will be updated when the manager start()s and call ice_init() + tsk_strupdate(&self->rtp.remote_ip, remote_ip); + self->rtp.remote_port = remote_port; + return 0; +} + +/** Sets remote parameters for rtcp session */ +int trtp_manager_set_rtcp_remote(trtp_manager_t* self, const char* remote_ip, tnet_port_t remote_port) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if(self->ice_ctx){ + TSK_DEBUG_WARN("Manually setting RTCP remote IP and Port while ICE is enabled"); + } + tsk_strupdate(&self->rtcp.remote_ip, remote_ip); + self->rtcp.remote_port = remote_port; + return 0; +} + +int trtp_manager_set_port_range(trtp_manager_t* self, uint16_t start, uint16_t stop) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->port_range.start = start; + self->port_range.stop = stop; + return 0; +} + +int trtp_manager_set_rtcweb_type_remote(trtp_manager_t* self, tmedia_rtcweb_type_t rtcweb_type) +{ + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->rtcweb_type.remote = rtcweb_type; + return 0; +} + +int trtp_manager_set_proxy_auto_detect(trtp_manager_t* self, tsk_bool_t auto_detect) +{ + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->proxy.auto_detect = auto_detect; + return 0; +} + +int trtp_manager_set_proxy_info(trtp_manager_t* self, enum tnet_proxy_type_e type, const char* host, tnet_port_t port, const char* login, const char* password) +{ + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (!self->proxy.info && !(self->proxy.info = tnet_proxyinfo_create())) { + return -2; + } + self->proxy.info->type = type; + self->proxy.info->port = port; + tsk_strupdate(&self->proxy.info->hostname, host); + tsk_strupdate(&self->proxy.info->username, login); + tsk_strupdate(&self->proxy.info->password, password); + return 0; +} + +/** Starts the RTP/RTCP manager */ +int trtp_manager_start(trtp_manager_t* self) +{ + int ret = 0; + int rcv_buf = (int)tmedia_defaults_get_rtpbuff_size(); + int snd_buf = (int)tmedia_defaults_get_rtpbuff_size(); +#if !TRTP_UNDER_WINDOWS_CE + int32_t dscp_rtp; +#endif + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tsk_safeobj_lock(self); + + if (self->is_started) { + goto bail; + } + + // Initialize transport with ICE context + if (self->ice_ctx && (ret = _trtp_manager_ice_init(self)) != 0) { + TSK_DEBUG_ERROR("_trtp_manager_ice_init() failed"); + goto bail; + } + + if(!self->transport && (ret = trtp_manager_prepare(self))){ + TSK_DEBUG_ERROR("Failed to prepare RTP/RTCP mamanger"); + goto bail; + } + + if(!self->transport || !self->transport->master){ + TSK_DEBUG_ERROR("RTP/RTCP manager not prepared"); + ret = -2; + goto bail; + } + + /* Proxy */ + // Proxy info + if ((ret = tnet_transport_set_proxy_auto_detect(self->transport, self->proxy.auto_detect))) { + TSK_DEBUG_ERROR("Failed to set proxy autodetect option"); + goto bail; + } + if (self->proxy.info) { + if ((ret = tnet_transport_set_proxy_info(self->transport, self->proxy.info->type, self->proxy.info->hostname, self->proxy.info->port, self->proxy.info->username, self->proxy.info->password))) { + TSK_DEBUG_ERROR("Failed to set proxy info"); + goto bail; + } + } + + /* Flush buffers and re-enable sockets */ + if(self->transport->master && self->is_socket_disabled){ + static char buff[1024]; + tsk_size_t guard_count = 0; +#if 0 + // re-enable sockets + _trtp_manager_enable_sockets(self); +#endif + + TSK_DEBUG_INFO("Start flushing RTP socket..."); + // Buffer should be empty ...but who know? + // rcv() should never block() as we are always using non-blocking sockets + while ((ret = (int)recv(self->transport->master->fd, buff, sizeof(buff), 0)) > 0 && ++guard_count < 0xF0){ + TSK_DEBUG_INFO("Flushing RTP Buffer %d", ret); + } + TSK_DEBUG_INFO("End flushing RTP socket"); + } + + /* enlarge socket buffer */ +#if !TRTP_UNDER_WINDOWS_CE + TSK_DEBUG_INFO("SO_RCVBUF = %d, SO_SNDBUF = %d", rcv_buf, snd_buf); + if ((ret = setsockopt(self->transport->master->fd, SOL_SOCKET, SO_RCVBUF, (char*)&rcv_buf, sizeof(rcv_buf)))) { + TNET_PRINT_LAST_ERROR("setsockopt(SOL_SOCKET, SO_RCVBUF, %d) has failed with error code %d", rcv_buf, ret); + } + if ((ret = setsockopt(self->transport->master->fd, SOL_SOCKET, SO_SNDBUF, (char*)&snd_buf, sizeof(snd_buf)))) { + TNET_PRINT_LAST_ERROR("setsockopt(SOL_SOCKET, SO_SNDBUF, %d) has failed with error code %d", snd_buf, ret); + } + dscp_rtp = (self->rtp.dscp << 2); + if ((ret = setsockopt(self->transport->master->fd, IPPROTO_IP, IP_TOS, (char*)&dscp_rtp, sizeof(dscp_rtp)))) { + TNET_PRINT_LAST_ERROR("setsockopt(IPPROTO_IP, IP_TOS) has failed with error code %d", ret); + } +#endif /* !TRTP_UNDER_WINDOWS_CE */ + + /* RTP */ + + // check remote IP address validity + if((tsk_striequals(self->rtp.remote_ip, "0.0.0.0") || tsk_striequals(self->rtp.remote_ip, "::"))) { // most likely loopback testing + tnet_ip_t source = {0}; + tsk_bool_t updated = tsk_false; + if(self->transport && self->transport->master){ + updated = (tnet_getbestsource(self->transport->master->ip, self->transport->master->port, self->transport->master->type, &source) == 0); + } + // Not allowed to send data to "0.0.0.0" + TSK_DEBUG_INFO("RTP remote IP contains not allowed value ...changing to '%s'", updated ? source : "oops"); + if(updated){ + tsk_strupdate(&self->rtp.remote_ip, source); + } + } + if((ret = tnet_sockaddr_init(self->rtp.remote_ip, self->rtp.remote_port, self->transport->master->type, &self->rtp.remote_addr))){ + tnet_transport_shutdown(self->transport); + TSK_OBJECT_SAFE_FREE(self->transport); + TSK_DEBUG_ERROR("Invalid RTP host:port [%s:%u]", self->rtp.remote_ip, self->rtp.remote_port); + goto bail; + } + TSK_DEBUG_INFO("rtp.remote_ip=%s, rtp.remote_port=%d, rtp.local_fd=%d", self->rtp.remote_ip, self->rtp.remote_port, self->transport->master->fd); + + /* RTCP */ + if(self->use_rtcp){ + tnet_fd_t local_rtcp_fd = self->rtcp.local_socket ? self->rtcp.local_socket->fd : -1; + if(local_rtcp_fd < 0 || self->use_rtcpmux){ // use RTP local port to send RTCP packets + local_rtcp_fd = self->transport->master->fd; + } + + if(!self->rtcp.remote_ip){ + self->rtcp.remote_ip = tsk_strdup(self->rtcp.remote_ip ? self->rtcp.remote_ip : self->rtp.remote_ip); + } + if(!self->rtcp.remote_port){ + self->rtcp.remote_port = self->rtcp.remote_port ? self->rtcp.remote_port : (self->use_rtcpmux ? self->rtp.remote_port : (self->rtp.remote_port + 1)); + } + + TSK_DEBUG_INFO("rtcp.remote_ip=%s, rtcp.remote_port=%d, rtcp.local_fd=%d", self->rtcp.remote_ip, self->rtcp.remote_port, local_rtcp_fd); + if((ret = tnet_sockaddr_init(self->rtcp.remote_ip, self->rtcp.remote_port, self->transport->master->type, &self->rtcp.remote_addr))){ + TSK_DEBUG_ERROR("Invalid RTCP host:port [%s:%u]", self->rtcp.remote_ip, self->rtcp.remote_port); + /* do not exit */ + } + + /* add RTCP socket to the transport */ + if(self->rtcp.local_socket){ + TSK_DEBUG_INFO("rtcp.local_ip=%s, rtcp.local_port=%d, rtcp.local_fd=%d", self->rtcp.local_socket->ip, self->rtcp.local_socket->port, self->rtcp.local_socket->fd); + if(ret == 0 && (ret = tnet_transport_add_socket(self->transport, self->rtcp.local_socket->fd, self->rtcp.local_socket->type, tsk_false/* do not take ownership */, tsk_true/* only Meaningful for tls*/, tsk_null))){ + TSK_DEBUG_ERROR("Failed to add RTCP socket"); + /* do not exit */ + } + } + /* create and start RTCP session */ + if(!self->rtcp.session && ret == 0){ + self->rtcp.session = trtp_rtcp_session_create_2(self->ice_ctx, self->rtp.ssrc.local, self->rtcp.cname); + } + if(self->rtcp.session){ + ret = trtp_rtcp_session_set_callback(self->rtcp.session, self->rtcp.cb.fun, self->rtcp.cb.usrdata); + ret = trtp_rtcp_session_set_app_bandwidth_max(self->rtcp.session, self->app_bw_max_upload, self->app_bw_max_download); + ret = trtp_rtcp_session_set_net_transport(self->rtcp.session, self->transport); + if((ret = trtp_rtcp_session_start(self->rtcp.session, local_rtcp_fd, (const struct sockaddr *)&self->rtcp.remote_addr))){ + TSK_DEBUG_ERROR("Failed to start RTCP session"); + goto bail; + } + } + } + + /*SRTP*/ +#if HAVE_SRTP + { + // activate SRTP (nothing will be done is srtp_mode is # "optional/mandatory") + // will also start the manager if we're using SDES mode + if((ret = _trtp_manager_srtp_activate(self, self->srtp_type))){ + goto bail; + } + + /* DTLS handshaking Timer */ + if (self->timer_mgr_global && self->srtp_state == trtp_srtp_state_activated && (self->srtp_type & tmedia_srtp_type_dtls) == tmedia_srtp_type_dtls) { + ret = tsk_timer_manager_start(self->timer_mgr_global); + self->dtls.timer_hanshaking.timeout = TRTP_DTLS_HANDSHAKING_TIMEOUT; + // start handshaking timer + // never mind if net work transport not started yet: the DTLS sockets will send the handshaking data by themself + self->dtls.timer_hanshaking.id = tsk_timer_manager_schedule(self->timer_mgr_global, self->dtls.timer_hanshaking.timeout, _trtp_transport_dtls_handshaking_timer_cb, self); + } + } +#endif /* HAVE_SRTP */ + + + /* start the transport if TURN is not active (otherwise TURN data will be received directly on RTP manager with channel headers) */ + if (!self->is_ice_turn_active && (ret = tnet_transport_start(self->transport))) { + TSK_DEBUG_ERROR("Failed to start the RTP/RTCP transport"); + goto bail; + } + + self->is_started = tsk_true; + +bail: + + tsk_safeobj_unlock(self); + + return ret; +} + +/* Encapsulate raw data into RTP packet and send it over the network +* Very IMPORTANT: For voice packets, the marker bits indicates the beginning of a talkspurt */ +tsk_size_t trtp_manager_send_rtp(trtp_manager_t* self, const void* data, tsk_size_t size, uint32_t duration, tsk_bool_t marker, tsk_bool_t last_packet) +{ + trtp_rtp_packet_t* packet; + tsk_size_t ret; + + if(!self || !self->transport || !data || !size){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + + /* check if transport is started */ + if(!self->is_started || !self->transport->master){ + TSK_DEBUG_WARN("RTP engine not ready yet"); + return 0; + } +#if HAVE_SRTP + /* check that SRTP engine is ready or disabled */ + if(self->srtp_state != trtp_srtp_state_none && self->srtp_state != trtp_srtp_state_started){ + TSK_DEBUG_WARN("SRTP engine not ready yet"); + return 0; + } +#endif + /* create packet with header */ + if(!(packet = trtp_rtp_packet_create(self->rtp.ssrc.local, ++self->rtp.seq_num, self->rtp.timestamp, self->rtp.payload_type, marker))){ + return 0; + } + if(last_packet){ + self->rtp.timestamp += duration; + } + + /* set data */ +#if 0 + if((packet->payload.data = tsk_calloc(size, sizeof(uint8_t)))){ + memcpy(packet->payload.data, data, size); + packet->payload.size = size; + } +#else + packet->payload.data_const = data; + packet->payload.size = size; +#endif + + ret = trtp_manager_send_rtp_packet(self, packet, tsk_false); + TSK_OBJECT_SAFE_FREE(packet); + return ret; +} + +// serialize, encrypt then send the data +tsk_size_t trtp_manager_send_rtp_packet(trtp_manager_t* self, const struct trtp_rtp_packet_s* packet, tsk_bool_t bypass_encrypt) +{ + int ret = 0; + tsk_size_t rtp_buff_pad_count = 0; + tsk_size_t xsize; + + /* check validity */ + if(!self || !packet){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + + tsk_safeobj_lock(self); + + // reset index + self->rtp.serial_buffer.index = 0; + + /* check if transport is started */ + if(!self->is_started || !self->transport || !self->transport->master){ + TSK_DEBUG_WARN("RTP engine not ready yet"); + ret = 0; + goto bail; + } +#if HAVE_SRTP + /* check that SRTP engine is ready or disabled */ + if(self->srtp_state != trtp_srtp_state_none && self->srtp_state != trtp_srtp_state_started){ + TSK_DEBUG_WARN("SRTP engine not ready yet"); + ret = 0; + goto bail; + } + if(self->srtp_ctx_neg_local && !bypass_encrypt){ + rtp_buff_pad_count = (SRTP_MAX_TRAILER_LEN + 0x04); + } +#endif /* HAVE_SRTP */ + + xsize = (trtp_rtp_packet_guess_serialbuff_size(packet) + rtp_buff_pad_count); + if(self->rtp.serial_buffer.size < xsize){ + if(!(self->rtp.serial_buffer.ptr = tsk_realloc(self->rtp.serial_buffer.ptr, xsize))){ + TSK_DEBUG_ERROR("Failed to allocate buffer with size = %d", (int)xsize); + self->rtp.serial_buffer.size = 0; + goto bail; + } + self->rtp.serial_buffer.size = xsize; + } + + /* serialize and send over the network */ + if ((ret = (int)trtp_rtp_packet_serialize_to(packet, self->rtp.serial_buffer.ptr, xsize))) { + void* data_ptr = self->rtp.serial_buffer.ptr; + int data_size = ret; +#if HAVE_SRTP + err_status_t status; + if(self->srtp_ctx_neg_local && !bypass_encrypt){ + if((status = srtp_protect(self->srtp_ctx_neg_local->rtp.session, data_ptr, &data_size)) != err_status_ok){ + TSK_DEBUG_ERROR("srtp_protect() failed with error code =%d", (int)status); + goto bail; + } + } +#endif + self->rtp.serial_buffer.index = data_size; // update index + if (/* number of bytes sent */(ret = (int)trtp_manager_send_rtp_raw(self, data_ptr, data_size)) > 0) { + // forward packet to the RTCP session + if (self->rtcp.session) { + trtp_rtcp_session_process_rtp_out(self->rtcp.session, packet, data_size); + } + } + else ret = 0; + } + else{ + TSK_DEBUG_ERROR("Failed to serialize RTP packet"); + } + +bail: + tsk_safeobj_unlock(self); + return ret; +} + +// send raw data "as is" without adding any RTP header or SRTP encryption +tsk_size_t trtp_manager_send_rtp_raw(trtp_manager_t* self, const void* data, tsk_size_t size) +{ + tsk_size_t ret = 0; + + if(!self || !self->transport || !self->transport->master || !data || !size){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + tsk_safeobj_lock(self); + if (self->is_ice_turn_active) { + // Send UDP/TCP/TLS buffer using TURN sockets + ret = (tnet_ice_ctx_send_turn_rtp(self->ice_ctx, data, size) == 0) ? size : 0; // returns #0 if ok + } + else { +#if 1 + ret = tnet_transport_sendto(self->transport, self->transport->master->fd, (const struct sockaddr *)&self->rtp.remote_addr, data, size); // returns number of sent bytes +#else + ret = tnet_sockfd_sendto(self->transport->master->fd, (const struct sockaddr *)&self->rtp.remote_addr, data, size); // returns number of sent bytes +#endif + } + tsk_safeobj_unlock(self); + return ret; +} + +int trtp_manager_get_bytes_count(trtp_manager_t* self, uint64_t* bytes_in, uint64_t* bytes_out) +{ + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (!self->is_started) { + TSK_DEBUG_INFO("trtp_manager_get_bytes_count() called before starting RTP manager... returning zeros"); + if (bytes_in) *bytes_in = 0; + if (bytes_out) *bytes_out = 0; + return 0; + } + + if (self->is_ice_turn_active) { + return tnet_ice_ctx_turn_get_bytes_count(self->ice_ctx, bytes_in, bytes_out); + } + return tnet_transport_get_bytes_count(self->transport, bytes_in, bytes_out); +} + +int trtp_manager_set_app_bandwidth_max(trtp_manager_t* self, int32_t bw_upload_kbps, int32_t bw_download_kbps) +{ + if(self){ + self->app_bw_max_upload = bw_upload_kbps; + self->app_bw_max_download = bw_download_kbps; + if(self->rtcp.session){ + return trtp_rtcp_session_set_app_bandwidth_max(self->rtcp.session, bw_upload_kbps, bw_download_kbps); + } + return 0; + } + return -1; +} + +int trtp_manager_signal_pkt_loss(trtp_manager_t* self, uint32_t ssrc_media, const uint16_t* seq_nums, tsk_size_t count) +{ + if(self && self->rtcp.session){ + return trtp_rtcp_session_signal_pkt_loss(self->rtcp.session, ssrc_media, seq_nums, count); + } + return -1; +} + +int trtp_manager_signal_frame_corrupted(trtp_manager_t* self, uint32_t ssrc_media) +{ + if(self && self->rtcp.session){ + return trtp_rtcp_session_signal_frame_corrupted(self->rtcp.session, ssrc_media); + } + return -1; +} + +int trtp_manager_signal_jb_error(trtp_manager_t* self, uint32_t ssrc_media) +{ + if(self && self->rtcp.session){ + return trtp_rtcp_session_signal_jb_error(self->rtcp.session, ssrc_media); + } + return -1; +} + +/** Stops the RTP/RTCP manager */ +int trtp_manager_stop(trtp_manager_t* self) +{ + int ret = 0; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + TSK_DEBUG_INFO("trtp_manager_stop()"); + + tsk_safeobj_lock(self); + + // We haven't started the ICE context which means we must not stop it + //if(self->ice_ctx){ + // ret = tnet_ice_ctx_stop(self->ice_ctx); + //} + + // callbacks + if (self->transport) { + ret = tnet_transport_set_callback(self->transport, tsk_null, tsk_null); + } + if (self->ice_ctx) { + ret = tnet_ice_ctx_rtp_callback(self->ice_ctx, tsk_null, tsk_null); + } + + // Stop the RTCP session first (will send BYE) + if(self->rtcp.session){ + ret = trtp_rtcp_session_stop(self->rtcp.session); + ret = trtp_rtcp_session_set_net_transport(self->rtcp.session, tsk_null); + } + + // Free transport to force next call to start() to create new one with new sockets + if (self->transport) { + tnet_socket_t *master_copy = tsk_object_ref(self->transport->master); // "tnet_transport_shutdown" will free the master + tnet_transport_shutdown(self->transport); +#if HAVE_SRTP + { + struct tnet_socket_s* sockets[] = { master_copy, self->rtcp.local_socket }; + // cancel DTLS handshaking timer + if (self->timer_mgr_global && self->dtls.timer_hanshaking.id != TSK_INVALID_TIMER_ID) { + tsk_timer_manager_cancel(self->timer_mgr_global, self->dtls.timer_hanshaking.id); + self->dtls.timer_hanshaking.id = TSK_INVALID_TIMER_ID; // invalidate timer id + self->dtls.timer_hanshaking.timeout = TRTP_DTLS_HANDSHAKING_TIMEOUT; // reset timeout + } + // destroy all SRTP contexts + _trtp_manager_srtp_set_enabled(self, self->srtp_type, sockets, sizeof(sockets) / sizeof(sockets[0]), tsk_false); + } +#endif /* HAVE_SRTP */ + TSK_OBJECT_SAFE_FREE(master_copy); + TSK_OBJECT_SAFE_FREE(self->transport); + } + // Free RTCP info to make sure these values will be updated in next start() + TSK_OBJECT_SAFE_FREE(self->rtcp.local_socket); + TSK_OBJECT_SAFE_FREE(self->rtcp.session); + self->rtcp.public_port = self->rtcp.remote_port = 0; + TSK_FREE(self->rtcp.public_ip); + TSK_FREE(self->rtcp.remote_ip); + + // reset default values + self->is_symetric_rtp_checked = self->is_symetric_rtcp_checked = tsk_false; + self->is_ice_neg_ok = tsk_false; + self->is_ice_turn_active = tsk_false; + self->is_socket_disabled = tsk_false; + + self->is_started = tsk_false; + + tsk_safeobj_unlock(self); + + return ret; +} + + + + +//================================================================================================= +// RTP manager object definition +// +static tsk_object_t* trtp_manager_ctor(tsk_object_t * self, va_list * app) +{ + trtp_manager_t *manager = (trtp_manager_t*)self; + if(manager){ + manager->port_range.start = tmedia_defaults_get_rtp_port_range_start(); + manager->port_range.stop = tmedia_defaults_get_rtp_port_range_stop(); + manager->is_force_symetric_rtp = tmedia_defaults_get_rtp_symetric_enabled(); + manager->app_bw_max_upload = INT_MAX; // INT_MAX or <=0 means undefined + manager->app_bw_max_download = INT_MAX; // INT_MAX or <=0 means undefined + + /* srtp */ +#if HAVE_SRTP + manager->srtp_type = tmedia_defaults_get_srtp_type(); + manager->srtp_mode = tmedia_defaults_get_srtp_mode(); + manager->dtls.timer_hanshaking.id = TSK_INVALID_TIMER_ID; + manager->dtls.timer_hanshaking.timeout = TRTP_DTLS_HANDSHAKING_TIMEOUT; +#endif /* HAVE_SRTP */ + + /* rtp */ + manager->rtp.timestamp = rand()^rand(); + manager->rtp.seq_num = rand()^rand(); + manager->rtp.ssrc.local = rand()^rand()^(int)tsk_time_epoch(); + manager->rtp.dscp = TRTP_DSCP_RTP_DEFAULT; + + /* rtcp */ + { + // use MD5 string to avoid padding issues + tsk_md5string_t md5 = { 0 }; + tsk_sprintf(&manager->rtcp.cname, "doubango.%llu", (tsk_time_now() + rand())); + tsk_md5compute(manager->rtcp.cname, tsk_strlen(manager->rtcp.cname), &md5); + tsk_strupdate(&manager->rtcp.cname, md5); + } + + /* timer */ + manager->timer_mgr_global = tsk_timer_mgr_global_ref(); + + tsk_safeobj_init(manager); + } + return self; +} + +static tsk_object_t* trtp_manager_dtor(tsk_object_t * self) +{ + trtp_manager_t *manager = self; + if(manager){ + /* callbacks */ + if (manager->ice_ctx) { + tnet_ice_ctx_rtp_callback(manager->ice_ctx, tsk_null, tsk_null); + } + if (manager->transport) { + + } + + /* stop */ + if (manager->is_started) { + trtp_manager_stop(manager); + } + + TSK_OBJECT_SAFE_FREE(manager->transport); + + TSK_FREE(manager->local_ip); + + /* rtp */ + TSK_FREE(manager->rtp.remote_ip); + TSK_FREE(manager->rtp.public_ip); + TSK_FREE(manager->rtp.serial_buffer.ptr); + + /* rtcp */ + TSK_OBJECT_SAFE_FREE(manager->rtcp.session); + TSK_FREE(manager->rtcp.remote_ip); + TSK_FREE(manager->rtcp.public_ip); + TSK_FREE(manager->rtcp.cname); + TSK_OBJECT_SAFE_FREE(manager->rtcp.local_socket); + + /* SRTP */ +#if HAVE_SRTP + { + int i; + + /* Timer */ + // cancel DTLS handshaking timer + if(manager->timer_mgr_global && manager->dtls.timer_hanshaking.id != TSK_INVALID_TIMER_ID){ + tsk_timer_manager_cancel(manager->timer_mgr_global, manager->dtls.timer_hanshaking.id); + manager->dtls.timer_hanshaking.id = TSK_INVALID_TIMER_ID; + } + + for(i = 0; i < 2; ++i){ + trtp_srtp_ctx_deinit(&manager->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][i]); + trtp_srtp_ctx_deinit(&manager->srtp_contexts[TRTP_SRTP_LINE_IDX_REMOTE][i]); + } + + /* SRTP-DTLS */ + TSK_FREE(manager->dtls.file_ca); + TSK_FREE(manager->dtls.file_pbk); + TSK_FREE(manager->dtls.file_pvk); + } +#endif /* HAVE_SRTP */ + + /* Timer manager */ + if(manager->timer_mgr_global){ + tsk_timer_mgr_global_unref(&manager->timer_mgr_global); + } + + /* ICE */ + if (manager->ice_ctx) { + TSK_OBJECT_SAFE_FREE(manager->ice_ctx); + } + + /* Proxy */ + TSK_OBJECT_SAFE_FREE(manager->proxy.info); + + tsk_safeobj_deinit(manager); + + TSK_DEBUG_INFO("*** RTP manager destroyed ***"); + } + + return self; +} + +static const tsk_object_def_t trtp_manager_def_s = +{ + sizeof(trtp_manager_t), + trtp_manager_ctor, + trtp_manager_dtor, + tsk_null, +}; +const tsk_object_def_t *trtp_manager_def_t = &trtp_manager_def_s; diff --git a/tinyRTP/src/trtp_srtp.c b/tinyRTP/src/trtp_srtp.c new file mode 100644 index 0000000..5e2d033 --- /dev/null +++ b/tinyRTP/src/trtp_srtp.c @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2012 Mamadou Diop + * Copyright (C) 2012-2013 Doubango Telecom <http://www.doubango.org> + * + * This file is part of Open Source Doubango Framework. + * + * DOUBANGO is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DOUBANGO is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DOUBANGO. + * + */ +/**@file trtp_srtp.c + */ +#include "tinyrtp/trtp_srtp.h" +#include "tinyrtp/trtp_manager.h" + +#if HAVE_SRTP + +extern err_status_t +crypto_get_random(unsigned char *buffer, unsigned int length); + +int trtp_srtp_ctx_internal_init(struct trtp_srtp_ctx_internal_xs* ctx, int32_t tag, trtp_srtp_crypto_type_t type, uint32_t ssrc) +{ + char* key_str = ctx->key_str; + err_status_t srtp_err; + tsk_size_t size; + + if (!ctx) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if (ctx->initialized) { + trtp_srtp_ctx_internal_deinit(ctx); + } + + ctx->tag = tag; + ctx->crypto_type = type; + if (!ctx->have_valid_key) { // use same key to avoid unseless SRTP re-negs (also fix interop-issues against buggy clients -reINVITEs-) + if ((srtp_err = crypto_get_random((unsigned char*)ctx->key_bin, sizeof(ctx->key_bin))) != err_status_ok) { + TSK_DEBUG_ERROR("crypto_get_random() failed"); + return -2; + } + size = tsk_base64_encode((const uint8_t*)ctx->key_bin, sizeof(ctx->key_bin), &key_str); + key_str[size] = '\0'; + ctx->have_valid_key = tsk_true; + } + + switch(ctx->crypto_type){ + case HMAC_SHA1_80: + { + crypto_policy_set_aes_cm_128_hmac_sha1_80(&ctx->policy.rtp); + crypto_policy_set_aes_cm_128_hmac_sha1_80(&ctx->policy.rtcp); + break; + } + case HMAC_SHA1_32: + default: + { + crypto_policy_set_aes_cm_128_hmac_sha1_32(&ctx->policy.rtp); + crypto_policy_set_aes_cm_128_hmac_sha1_80(&ctx->policy.rtcp); // RTCP always 80 + break; + } + } + + ctx->policy.key = (unsigned char*)ctx->key_bin; + ctx->policy.ssrc.type = ssrc_any_outbound; + ctx->policy.ssrc.value = ssrc; + ctx->policy.window_size = 2048; + ctx->policy.allow_repeat_tx = 1; + if ((srtp_err = srtp_create(&ctx->session, &ctx->policy)) != err_status_ok) { + TSK_DEBUG_ERROR("srtp_create() failed"); + return -3; + } + ctx->initialized = tsk_true; + return 0; +} + +int trtp_srtp_ctx_internal_deinit(struct trtp_srtp_ctx_internal_xs* ctx) +{ + if(!ctx){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if(ctx->initialized){ + /*err_status_t srtp_err =*/ srtp_dealloc(ctx->session); + memset(&ctx->policy, 0, sizeof(ctx->policy)); + ctx->initialized = tsk_false; + } + return 0; +} + +int trtp_srtp_ctx_init(trtp_srtp_ctx_xt* ctx, int32_t tag, trtp_srtp_crypto_type_t type, uint32_t ssrc) +{ + int ret; + if(!ctx){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if((ret = trtp_srtp_ctx_internal_init(&ctx->rtp, tag, type, ssrc))){ + return ret; + } + return trtp_srtp_ctx_internal_init(&ctx->rtcp, tag, type, ssrc); +} + +int trtp_srtp_ctx_deinit(trtp_srtp_ctx_xt* ctx) +{ + int ret; + if(!ctx){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if((ret = trtp_srtp_ctx_internal_deinit(&ctx->rtp))){ + return ret; + } + return trtp_srtp_ctx_internal_deinit(&ctx->rtcp); +} + +int trtp_srtp_match_line(const char* crypto_line, int32_t* tag, int32_t* crypto_type, char* key, tsk_size_t key_size) +{ + char* copyptr = tsk_strdup(crypto_line); // "strtok_r" will insert "\0" and modify the string + char* saveptr = tsk_null; + char* v = tsk_strtok_r(copyptr, " :|;", &saveptr); + int32_t k = 0; + int ret = -0xF0; + while(v){ + switch(k){ + case 0: + { + if(tag){ + *tag = atoi(v); + } + break; + } + case 1: + { + if(tsk_striequals(v, TRTP_SRTP_AES_CM_128_HMAC_SHA1_80)){ + if(crypto_type){ + *crypto_type = HMAC_SHA1_80; + } + } + else if(tsk_striequals(v, TRTP_SRTP_AES_CM_128_HMAC_SHA1_32)){ + if(crypto_type){ + *crypto_type = HMAC_SHA1_32; + } + } + else { + ret = -0xFF; goto bail; + } + break; + } + case 2: + { + if(!tsk_striequals(v, "inline")){ + ret = -0xFF; goto bail; + } + break; + } + case 3: + { + if(key && key_size){ + memset(key, 0, key_size); + memcpy(key, v, TSK_MIN(key_size, tsk_strlen(v))); + } + ret = 0; goto bail; + } + } + ++k; + v = tsk_strtok_r(tsk_null, " :|;", &saveptr); + } +bail: + TSK_FREE(copyptr); + return ret; +} + +tsk_size_t trtp_srtp_get_local_contexts(trtp_manager_t* rtp_mgr, const struct trtp_srtp_ctx_xs ** contexts, tsk_size_t contexts_count) +{ + tsk_size_t ret = 0; + if(!rtp_mgr || !contexts){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + + if (contexts_count > ret && rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_80].rtp.initialized) { + contexts[ret++] = &rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_80]; + } + if (contexts_count > ret && rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_32].rtp.initialized) { + contexts[ret++] = &rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_32]; + } + return ret; +} + +int trtp_srtp_set_crypto(struct trtp_manager_s* rtp_mgr, const char* crypto_line, int32_t idx) +{ + //e.g. 2 F8_128_HMAC_SHA1_80 inline:MTIzNDU2Nzg5QUJDREUwMTIzNDU2Nzg5QUJjZGVm|2^20|1:4;inline:QUJjZGVmMTIzNDU2Nzg5QUJDREUwMTIzNDU2Nzg5|2^20|2:4" + trtp_srtp_ctx_xt* srtp_ctx; + int ret; + uint8_t *key_bin; + err_status_t srtp_err; + int32_t tag, crypto_type; + char key_str[SRTP_MAX_KEY_LEN + 1]; + + memset(key_str, 0, sizeof(key_str)); + + if ((ret = trtp_srtp_match_line(crypto_line, &tag, &crypto_type, key_str, sizeof(key_str) - 1))) { + return ret; + } + + srtp_ctx = &rtp_mgr->srtp_contexts[idx][crypto_type]; + ret = trtp_srtp_ctx_deinit(srtp_ctx); + + srtp_ctx->rtp.tag = tag; + srtp_ctx->rtp.crypto_type = (trtp_srtp_crypto_type_t)crypto_type; + memcpy(srtp_ctx->rtp.key_str, key_str, sizeof(srtp_ctx->rtp.key_str)); + + switch(srtp_ctx->rtp.crypto_type){ + case HMAC_SHA1_80: + { + crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->rtp.policy.rtp); + crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->rtp.policy.rtcp); + if (idx == TRTP_SRTP_LINE_IDX_REMOTE) { + trtp_srtp_ctx_deinit(&rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_32]); + rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_80].rtp.tag = + rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_80].rtcp.tag = srtp_ctx->rtp.tag; + } + break; + } + case HMAC_SHA1_32: + { + crypto_policy_set_aes_cm_128_hmac_sha1_32(&srtp_ctx->rtp.policy.rtp); + crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->rtp.policy.rtcp); // RTCP always 80 + if (idx == TRTP_SRTP_LINE_IDX_REMOTE) { + trtp_srtp_ctx_deinit(&rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_80]); + rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_32].rtp.tag = + rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][HMAC_SHA1_32].rtcp.tag = srtp_ctx->rtp.tag; + } + break; + } + default: break; + } + + key_bin = (unsigned char*)srtp_ctx->rtp.key_bin; + tsk_base64_decode((const uint8_t*)srtp_ctx->rtp.key_str, (tsk_size_t)tsk_strlen(srtp_ctx->rtp.key_str), (char**)&key_bin); + srtp_ctx->rtp.policy.key = key_bin; + srtp_ctx->rtp.policy.ssrc.type = idx == TRTP_SRTP_LINE_IDX_REMOTE ? ssrc_any_inbound : ssrc_any_outbound; + srtp_ctx->rtp.policy.window_size = 2048; + srtp_ctx->rtp.policy.allow_repeat_tx = 1; + if ((srtp_err = srtp_create(&srtp_ctx->rtp.session, &srtp_ctx->rtp.policy)) != err_status_ok) { + TSK_DEBUG_ERROR("srtp_create() failed: %d", srtp_err); + return -3; + } + srtp_ctx->rtp.initialized = tsk_true; + return 0; +} + +int trtp_srtp_set_key_and_salt(trtp_manager_t* rtp_mgr, trtp_srtp_crypto_type_t crypto_type, const void* key, tsk_size_t key_size, const void* salt, tsk_size_t salt_size, int32_t idx, tsk_bool_t is_rtp) +{ + int ret; + trtp_srtp_ctx_internal_xt* srtp_ctx; + err_status_t srtp_err; + if (!rtp_mgr || !key || !key_size || !salt || !salt_size) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + srtp_ctx = is_rtp ? &rtp_mgr->srtp_contexts[idx][crypto_type].rtp : &rtp_mgr->srtp_contexts[idx][crypto_type].rtcp; + if ((ret = trtp_srtp_ctx_internal_deinit(srtp_ctx))) { + return ret; + } + + switch ((srtp_ctx->crypto_type = crypto_type)) { + case HMAC_SHA1_80: + default: + { + crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->policy.rtp); + crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->policy.rtcp); + break; + } + case HMAC_SHA1_32: + { + crypto_policy_set_aes_cm_128_hmac_sha1_32(&srtp_ctx->policy.rtp); + crypto_policy_set_aes_cm_128_hmac_sha1_80(&srtp_ctx->policy.rtcp); // always 80 + break; + } + } + + memcpy(srtp_ctx->key_bin, key, key_size); +#if HAVE_APPEND_SALT_TO_KEY + append_salt_to_key(srtp_ctx->key_bin, key_size, (void*)salt, salt_size); +#else + memcpy(&srtp_ctx->key_bin[key_size], salt, salt_size); +#endif + + srtp_ctx->policy.key = (unsigned char *)srtp_ctx->key_bin; + srtp_ctx->policy.ssrc.type = idx == TRTP_SRTP_LINE_IDX_REMOTE ? ssrc_any_inbound : ssrc_any_outbound; + srtp_ctx->policy.window_size = 2048; + srtp_ctx->policy.allow_repeat_tx = 1; + if((srtp_err = srtp_create(&srtp_ctx->session, &srtp_ctx->policy)) != err_status_ok){ + TSK_DEBUG_ERROR("srtp_create() failed: %d", srtp_err); + return -3; + } + srtp_ctx->initialized = tsk_true; + return 0; +} + +tsk_bool_t trtp_srtp_is_initialized(trtp_manager_t* rtp_mgr) +{ + if (!rtp_mgr) { + return tsk_false; + } + return ((rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][0].rtp.initialized || rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_LOCAL][1].rtp.initialized) + && rtp_mgr->srtp_contexts[TRTP_SRTP_LINE_IDX_REMOTE][0].rtp.initialized); +} + +tsk_bool_t trtp_srtp_is_started(trtp_manager_t* rtp_mgr) +{ + if (!rtp_mgr) { + TSK_DEBUG_ERROR("Invalid argument"); + return tsk_false; + } + return (rtp_mgr ->srtp_ctx_neg_remote && rtp_mgr ->srtp_ctx_neg_local); +} + +#endif /* HAVE_SRTP */
\ No newline at end of file diff --git a/tinyRTP/test/test.c b/tinyRTP/test/test.c new file mode 100644 index 0000000..f0f15f6 --- /dev/null +++ b/tinyRTP/test/test.c @@ -0,0 +1,65 @@ +/* +* Copyright (C) 2009 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#include <string.h> +#include <stdlib.h> + +#include "tinyrtp.h" + +#define LOOP 1 + +#define RUN_TEST_ALL 0 +#define RUN_TEST_PARSER 0 +#define RUN_TEST_MANAGER 1 + +#include "test_parser.h" +#include "test_manager.h" + + + +#ifdef _WIN32_WCE +int _tmain(int argc, _TCHAR* argv[]) +#else +int main() +#endif +{ + tnet_startup(); + + do{ + /* Print copyright information */ + printf("Doubango Project\nCopyright (C) 2009-2010 Mamadou Diop \n\n"); + +#if RUN_TEST_PARSER || RUN_TEST_ALL + test_parser(); +#endif + +#if RUN_TEST_MANAGER || RUN_TEST_ALL + test_manager(); +#endif + + } + while(LOOP); + + tnet_cleanup(); + + return 0; +} + diff --git a/tinyRTP/test/test.vcproj b/tinyRTP/test/test.vcproj new file mode 100644 index 0000000..53adaaf --- /dev/null +++ b/tinyRTP/test/test.vcproj @@ -0,0 +1,200 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9.00" + Name="test" + ProjectGUID="{A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}" + RootNamespace="test" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\..\thirdparties\win32\include;..\..\tinyRTP\include;..\..\tinySAK\src;..\..\tinyNET\src" + PreprocessorDefinitions="DEBUG_LEVEL=DEBUG_LEVEL_INFO;WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + WarnAsError="true" + DebugInformationFormat="4" + CompileAs="1" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="$(OutDir)\tinySAK.lib $(OutDir)\tinyNET.lib $(OutDir)\tinyRTP.lib" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="2" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + CompileAs="1" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="source" + > + <File + RelativePath=".\test.c" + > + </File> + </Filter> + <Filter + Name="include" + > + </Filter> + <Filter + Name="tests" + > + <File + RelativePath=".\test_manager.h" + > + </File> + <File + RelativePath=".\test_parser.h" + > + </File> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tinyRTP/test/test_manager.h b/tinyRTP/test/test_manager.h new file mode 100644 index 0000000..29c2dcc --- /dev/null +++ b/tinyRTP/test/test_manager.h @@ -0,0 +1,69 @@ +/* +* Copyright (C) 2009 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef _TEST_MANAGER_H_ +#define _TEST_MANAGER_H_ + +void test_manager() +{ + tsk_size_t i; + trtp_manager_t* manager; + + if(!(manager = trtp_manager_create(tsk_true, "192.168.0.12", tsk_false))){ + goto bail; + } + + trtp_manager_set_payload_type(manager, 8); + + /* Prepare: this will allow you to generate local port and ip */ + if(trtp_manager_prepare(manager)){ + goto bail; + } + + /* set remote parameters (rtcp ip:port not mandator, could be retrieved from rtp values) */ + if(trtp_manager_set_rtp_remote(manager, "192.168.0.13", 5081)){ + goto bail; + } + if(trtp_manager_set_rtcp_remote(manager, "192.168.0.13", 2861)){ + goto bail; + } + + /* start */ + if(trtp_manager_start(manager)){ + goto bail; + } + + /* send data */ + for(i=0;i<2; i++){ + if(trtp_manager_send_rtp(manager, "test", tsk_strlen("test"), 160, tsk_true)){ + goto bail; + } + } + + getchar(); + +bail: + /* stop and destroy */ + TSK_OBJECT_SAFE_FREE(manager); +} + +#endif /* _TEST_MANAGER_H_ */ + diff --git a/tinyRTP/test/test_parser.h b/tinyRTP/test/test_parser.h new file mode 100644 index 0000000..a65b816 --- /dev/null +++ b/tinyRTP/test/test_parser.h @@ -0,0 +1,331 @@ +/* +* Copyright (C) 2009 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with DOUBANGO. +* +*/ +#ifndef _TEST_PARSER_H_ +#define _TEST_PARSER_H_ + +char packet_0[] = { +0x80, 0x88, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0, +0xd2, 0xbd, 0x4e, 0x3e, 0xdc, 0xde, 0xc4, 0xc5, +0xdc, 0xd0, 0xd5, 0x51, 0x53, 0x5d, 0x5f, 0x5b, +0x46, 0x46, 0x46, 0x5b, 0x44, 0x41, 0x42, 0x4f, +0x42, 0x47, 0x42, 0x43, 0x59, 0x58, 0x59, 0x5f, +0x5f, 0x52, 0x59, 0x44, 0x44, 0x5f, 0x51, 0x54, +0x55, 0x55, 0x51, 0x56, 0x50, 0x52, 0x5e, 0x58, +0x5d, 0x52, 0x52, 0x50, 0x57, 0x54, 0xd4, 0xd6, +0xd5, 0x51, 0x53, 0x57, 0xd6, 0xd6, 0xd0, 0xd7, +0x57, 0x56, 0x57, 0xd0, 0xd3, 0xd6, 0xd5, 0x55, +0x51, 0x50, 0xd6, 0xdf, 0xd2, 0xd1, 0xd4, 0xd6, +0xdc, 0xdb, 0xda, 0xdd, 0xd6, 0x55, 0xdc, 0xd0, +0xd4, 0x5d, 0x44, 0x5c, 0x56, 0xd6, 0xd5, 0xd4, +0xd5, 0xd7, 0x50, 0xd4, 0x51, 0xd0, 0x61, 0x6f, +0x76, 0xfe, 0xef, 0xf7, 0x77, 0x66, 0x50, 0xff, +0xe5, 0xd7, 0x74, 0x4a, 0xc9, 0xf9, 0xf7, 0x5c, +0x76, 0x5f, 0xf5, 0xf3, 0xdd, 0x4e, 0x42, 0xd8, +0xf7, 0xc9, 0x50, 0x44, 0x50, 0xcd, 0xc9, 0xd4, +0x4d, 0x41, 0x57, 0xd1, 0x51, 0x58, 0x44, 0x52, +0xd3, 0xd1, 0x50, 0x58, 0x5b, 0x55, 0xd4, 0x53, +0x59, 0x43, 0x47, 0x5f, 0x51, 0x5d, 0x56, 0xd2, +0xde, 0xd7, 0x52, 0xd5 }; +char packet_1[] = { +0x80, 0x08, 0x00, 0x02, 0x00, 0x00, 0x01, 0x40, +0xd2, 0xbd, 0x4e, 0x3e, 0xd2, 0xc7, 0xc5, 0xd3, +0xd1, 0xd6, 0xd5, 0x55, 0xd2, 0xd2, 0xd3, 0xdf, +0xc4, 0xd9, 0xdb, 0xc6, 0xc6, 0xc9, 0xc1, 0xf5, +0xcf, 0xf0, 0xcb, 0xe4, 0x7d, 0x14, 0x7a, 0xfd, +0xfd, 0x62, 0x63, 0x41, 0xee, 0xe7, 0x40, 0x6c, +0x46, 0xf8, 0xf3, 0x74, 0x7b, 0x58, 0xf3, 0xf1, +0xd6, 0x5d, 0xc7, 0xfd, 0xff, 0xf5, 0x55, 0xd9, +0xcb, 0xcb, 0xde, 0xd1, 0xda, 0xca, 0xcd, 0xc5, +0xc4, 0x41, 0x72, 0x41, 0x55, 0xda, 0x54, 0x5c, +0x40, 0x59, 0xd1, 0x5f, 0x4b, 0x40, 0x51, 0x52, +0x45, 0x4b, 0x77, 0x49, 0x4e, 0x4e, 0x48, 0x4a, +0x75, 0x47, 0x75, 0x7b, 0x72, 0x42, 0x58, 0x4c, +0x46, 0xd5, 0xc2, 0xd1, 0x5d, 0x44, 0x50, 0xd0, +0x53, 0x43, 0x41, 0x52, 0x53, 0x5a, 0x44, 0x50, +0xdd, 0x45, 0x65, 0x7d, 0x74, 0x4c, 0x70, 0x43, +0x53, 0xc4, 0xc5, 0x5b, 0x5a, 0x5c, 0x51, 0x4f, +0x70, 0x4a, 0x58, 0x5e, 0x75, 0x76, 0x40, 0x57, +0x51, 0x46, 0x5e, 0x5c, 0xd5, 0x5c, 0x5c, 0x5e, +0x5e, 0x4d, 0x41, 0x44, 0xd4, 0xd6, 0xd5, 0x52, +0x54, 0xdd, 0xd7, 0xd3, 0x56, 0x51, 0x55, 0xdf, +0x54, 0xd1, 0xd2, 0xda }; +char packet_2[] = { +0x80, 0x08, 0x00, 0x03, 0x00, 0x00, 0x01, 0xe0, +0xd2, 0xbd, 0x4e, 0x3e, 0xdb, 0xd1, 0xd5, 0xd0, +0xd2, 0xd3, 0x57, 0xd5, 0xd6, 0xd6, 0xd1, 0xd5, +0xd1, 0xdd, 0xd5, 0xd1, 0xd1, 0xd4, 0x5f, 0x56, +0x5f, 0x5a, 0x53, 0x59, 0x5c, 0x5f, 0x5d, 0x5d, +0xd6, 0xd0, 0xd8, 0x54, 0xc4, 0xd9, 0xc6, 0xdc, +0x57, 0x56, 0x56, 0x57, 0x51, 0x54, 0x53, 0x57, +0x59, 0x44, 0x44, 0x5e, 0x45, 0x45, 0x5a, 0x5e, +0x53, 0x58, 0x4c, 0x45, 0x59, 0x45, 0x5b, 0x57, +0x52, 0x5e, 0x5e, 0x59, 0x5c, 0x51, 0xd7, 0xd0, +0xc5, 0xc1, 0xd8, 0xc1, 0xcd, 0xcd, 0xc2, 0xcd, +0xc6, 0xcc, 0xc0, 0xc6, 0xc1, 0xc9, 0xcb, 0xdb, +0xd0, 0xdd, 0xc6, 0xd8, 0xd5, 0x5f, 0x41, 0x46, +0x4f, 0x74, 0x76, 0x70, 0x72, 0x7f, 0x72, 0x77, +0x49, 0x4a, 0x76, 0x4a, 0x44, 0x45, 0x44, 0x59, +0x50, 0x55, 0xd3, 0xd9, 0xcc, 0xf7, 0xf1, 0xca, +0xc2, 0xf7, 0xf8, 0xe7, 0xe7, 0xe2, 0xee, 0xe9, +0xed, 0xe4, 0xc2, 0xcb, 0xf7, 0xd2, 0x48, 0x76, +0x45, 0x77, 0x64, 0x64, 0x4a, 0x51, 0x46, 0x41, +0xdd, 0xc2, 0xd4, 0x51, 0x50, 0x4a, 0x74, 0x71, +0x7e, 0x61, 0x67, 0x7e, 0x67, 0x60, 0x67, 0x77, +0x45, 0x43, 0x5f, 0xc8 }; +char packet_3[] = { +0x80, 0x08, 0x2c, 0x43, 0x11, 0x47, 0x31, 0xa2, +0x58, 0xf3, 0x3d, 0xea, 0x53, 0x45, 0x56, 0x47, +0x5f, 0xd4, 0xd4, 0x55, 0x56, 0x58, 0x40, 0x53, +0x54, 0xd5, 0xc7, 0xc4, 0x57, 0xd5, 0xdd, 0xd9, +0xde, 0xdd, 0xdc, 0xd4, 0x55, 0x54, 0xdc, 0xcf, +0xc7, 0x5e, 0x43, 0x5a, 0x5a, 0x43, 0x5b, 0x51, +0x54, 0x51, 0xd6, 0xd4, 0x5b, 0x53, 0xd3, 0x57, +0xd5, 0xc0, 0xc7, 0x53, 0x5d, 0xd1, 0xc5, 0xc1, +0xd9, 0x5d, 0x57, 0xdc, 0xd0, 0xd0, 0x54, 0x45, +0x44, 0x42, 0x45, 0xd0, 0xd4, 0xd5, 0xd9, 0xc7, +0x57, 0x5e, 0x5f, 0x47, 0x56, 0xc5, 0xca, 0xc5, +0xd3, 0xdf, 0x57, 0x43, 0x4b, 0x5a, 0x5c, 0x5f, +0x45, 0x52, 0xd0, 0xc7, 0xcb, 0xc9, 0xc5, 0x5f, +0x47, 0x57, 0x55, 0xdc, 0xc7, 0x5d, 0x4b, 0x5a, +0xdb, 0xcf, 0xc5, 0x52, 0x43, 0x5a, 0x56, 0xde, +0xdf, 0xd6, 0xd5, 0x45, 0x5b, 0xdc, 0xdb, 0x55, +0x52, 0x54, 0x51, 0xd5, 0xc4, 0xdf, 0xd1, 0x56, +0x51, 0xd5, 0xd3, 0xde, 0x54, 0x44, 0x46, 0x52, +0x56, 0x5b, 0x5a, 0xd3, 0xc3, 0xdf, 0x57, 0xd1, +0xd9, 0xd6, 0x5f, 0x5f, 0xd4, 0xd6, 0xd5, 0xd1, +0x53, 0xd4, 0xc1, 0xdb, 0xdd, 0xd8, 0xd7, 0x47, +0x4b, 0x75, 0x4c, 0x5c }; +char packet_4[] = { +0x80, 0x08, 0x00, 0x04, 0x00, 0x00, 0x02, 0x80, +0xd2, 0xbd, 0x4e, 0x3e, 0xff, 0xf3, 0xff, 0xe1, +0xe7, 0xe4, 0xe5, 0xe2, 0xec, 0xef, 0xeb, 0xe9, +0x95, 0x97, 0xf8, 0xcc, 0xe4, 0xf9, 0x58, 0x6d, +0x72, 0x46, 0x7c, 0x62, 0x60, 0x42, 0x40, 0x70, +0x4e, 0xd3, 0xcb, 0xda, 0xd9, 0x54, 0x57, 0xd6, +0x4a, 0x62, 0x6e, 0x64, 0x60, 0x15, 0x15, 0x63, +0x67, 0x62, 0x60, 0x7f, 0x46, 0x46, 0x40, 0xd5, +0xcf, 0xf6, 0xc9, 0xcb, 0xf0, 0xfd, 0xf8, 0xe6, +0xec, 0xe9, 0x94, 0x91, 0x96, 0xf7, 0xd0, 0xe7, +0xf3, 0x7d, 0x6b, 0x7d, 0x5d, 0x65, 0x68, 0x61, +0x5d, 0x5c, 0x72, 0x43, 0xcd, 0xf6, 0xdf, 0xd2, +0xd2, 0x51, 0xdf, 0x49, 0x62, 0x14, 0x68, 0x6e, +0x10, 0x11, 0x69, 0x64, 0x61, 0x6d, 0x64, 0x75, +0x44, 0x74, 0x5e, 0xcf, 0xff, 0xf1, 0xc1, 0xf7, +0xf6, 0xe5, 0xe4, 0xe6, 0xe9, 0x91, 0x93, 0x90, +0x95, 0xce, 0xe5, 0xe0, 0xc7, 0x66, 0x62, 0x45, +0x75, 0x63, 0x6e, 0x79, 0x55, 0x49, 0x71, 0x53, +0xca, 0xcb, 0xd0, 0xdb, 0xd3, 0xd5, 0x5b, 0x67, +0x6b, 0x15, 0x61, 0x69, 0x17, 0x14, 0x6c, 0x66, +0x6f, 0x62, 0x7f, 0x48, 0x74, 0x75, 0x59, 0xd0, +0xdb, 0x54, 0xd7, 0xc3 }; +char packet_5[] = { +0x80, 0x08, 0x2c, 0x44, 0x11, 0x47, 0x32, 0x42, +0x58, 0xf3, 0x3d, 0xea, 0x54, 0xd5, 0xde, 0xc4, +0xc0, 0xce, 0xd8, 0xd5, 0xdc, 0xde, 0xd4, 0xd9, +0x54, 0x4d, 0x74, 0x47, 0x44, 0x58, 0xc7, 0xc3, +0xc4, 0xd4, 0x55, 0x50, 0x43, 0x45, 0xd4, 0xc7, +0xcf, 0xc0, 0x55, 0x42, 0x48, 0x76, 0x4f, 0xd7, +0xc7, 0xcf, 0xc6, 0xc6, 0xf5, 0xc6, 0x53, 0x5a, +0x42, 0x5e, 0x51, 0xd6, 0xc4, 0xde, 0x56, 0x42, +0x42, 0x5e, 0xd7, 0xd4, 0xdb, 0xcb, 0xda, 0xd6, +0xd7, 0x53, 0x47, 0x4d, 0xd4, 0xdf, 0xdc, 0xd5, +0x5a, 0x59, 0x5a, 0x5a, 0xd5, 0xc4, 0xd7, 0x55, +0xc3, 0xce, 0xde, 0xd2, 0xd6, 0x46, 0x71, 0x7c, +0x4c, 0xd8, 0xf5, 0xf7, 0xcf, 0x54, 0x56, 0xd5, +0xdc, 0xda, 0xd7, 0xd7, 0x57, 0x5a, 0x5c, 0x52, +0x47, 0x5c, 0xd4, 0xd3, 0xd4, 0x47, 0x42, 0x4d, +0x54, 0xcf, 0xf3, 0xf0, 0xc0, 0xdb, 0xd1, 0x50, +0x47, 0x5e, 0x58, 0x49, 0x43, 0x5a, 0x54, 0xdc, +0x56, 0x56, 0xd9, 0xc8, 0xc2, 0xd2, 0x5d, 0x5a, +0x5a, 0x49, 0x40, 0x57, 0xda, 0xdb, 0xd6, 0x55, +0xdd, 0xcf, 0xdb, 0x5c, 0x51, 0xd0, 0xd5, 0x5f, +0x53, 0xd8, 0xc3, 0xc3, 0xd0, 0x5c, 0x5b, 0x44, +0x4e, 0x74, 0x4f, 0x56 }; +char packet_6[] = { +0x80, 0x08, 0x00, 0x05, 0x00, 0x00, 0x03, 0x20, +0xd2, 0xbd, 0x4e, 0x3e, 0xca, 0xf0, 0xfd, 0xe2, +0xea, 0x96, 0x91, 0x91, 0xe7, 0xd9, 0xf9, 0xf9, +0x48, 0x6b, 0x61, 0x74, 0x66, 0x15, 0x69, 0x74, +0x42, 0x7d, 0x75, 0xd2, 0xf7, 0xc8, 0xc3, 0xd1, +0x5f, 0x5f, 0x7d, 0x6f, 0x15, 0x6d, 0x6e, 0x16, +0x16, 0x68, 0x61, 0x62, 0x6c, 0x66, 0x79, 0x7d, +0x7f, 0x77, 0x5c, 0xd9, 0xdb, 0x5e, 0x55, 0xcf, +0xf1, 0xf0, 0xe5, 0xef, 0x95, 0x96, 0x93, 0x97, +0xcd, 0xc3, 0xe4, 0xf7, 0x64, 0x15, 0x7a, 0x73, +0x60, 0x68, 0x64, 0xd1, 0x53, 0x4e, 0x5d, 0xf7, +0xf9, 0xf3, 0xf5, 0xd5, 0xd4, 0x58, 0x67, 0x6e, +0x6c, 0x61, 0x68, 0x14, 0x68, 0x61, 0x64, 0x63, +0x61, 0x79, 0x75, 0x4b, 0x4f, 0x44, 0xd7, 0xcb, +0xc0, 0xd8, 0xf6, 0xe4, 0xe1, 0xe7, 0xed, 0x97, +0x91, 0x93, 0x9d, 0x96, 0xff, 0xfd, 0xe5, 0xc7, +0x66, 0x67, 0x4d, 0x4e, 0x64, 0x62, 0x71, 0x54, +0xd5, 0x5f, 0xd0, 0xce, 0xce, 0xdf, 0x5d, 0x73, +0x72, 0x73, 0x6c, 0x15, 0x6d, 0x6d, 0x15, 0x14, +0x69, 0x63, 0x62, 0x62, 0x66, 0x78, 0x72, 0x76, +0x43, 0x5b, 0xd5, 0xc0, 0xd9, 0xdd, 0xca, 0xf8, +0xe4, 0xe7, 0xef, 0x96 }; +char packet_7[] = { +0x80, 0x08, 0x2c, 0x45, 0x11, 0x47, 0x32, 0xe2, +0x58, 0xf3, 0x3d, 0xea, 0xdd, 0xd9, 0xd2, 0xd6, +0xd0, 0xd6, 0xdf, 0xd7, 0xd7, 0x54, 0xd5, 0x57, +0x57, 0xd0, 0xd4, 0x55, 0xd5, 0xd8, 0xdb, 0xc0, +0xc2, 0x53, 0x58, 0x5d, 0x57, 0x5e, 0x47, 0x41, +0x4a, 0x5f, 0xde, 0xd9, 0x55, 0xd0, 0xc5, 0xdd, +0xd3, 0xdd, 0xc6, 0xc1, 0xd1, 0x52, 0x5b, 0xd1, +0xcc, 0xd9, 0x43, 0x7a, 0x78, 0x4e, 0x55, 0xd2, +0xc5, 0xc3, 0xda, 0xc5, 0xd4, 0xd3, 0xc7, 0xd2, +0xd9, 0xdb, 0x55, 0x45, 0x59, 0x51, 0x59, 0xd4, +0xc2, 0xc6, 0xd0, 0xd1, 0x59, 0x48, 0x40, 0x47, +0x5a, 0x55, 0xd2, 0xdc, 0xd9, 0xc9, 0xf6, 0xd9, +0x42, 0x72, 0x40, 0xd1, 0x50, 0xd7, 0xc4, 0xc9, +0xc5, 0xc1, 0xc3, 0x54, 0x5b, 0x48, 0x43, 0x57, +0xd3, 0xd1, 0x5c, 0x47, 0x47, 0x49, 0x47, 0x59, +0xd1, 0xf5, 0xd8, 0xd6, 0xdd, 0xd8, 0x5c, 0x53, +0xc7, 0xc9, 0xc9, 0xc0, 0xd4, 0x4d, 0x4f, 0x47, +0x50, 0xdf, 0xce, 0xd8, 0x5d, 0x43, 0x40, 0x5f, +0xd1, 0x56, 0x58, 0xd0, 0xd0, 0xd0, 0xdb, 0x55, +0x5b, 0x5b, 0xd4, 0xda, 0xc0, 0xc6, 0xd4, 0x58, +0x5a, 0xd8, 0xc7, 0xdf, 0x54, 0x4a, 0x71, 0x43, +0xd0, 0xc3, 0xc9, 0xde }; +char packet_8[] = { +0x80, 0x08, 0x00, 0x06, 0x00, 0x00, 0x03, 0xc0, +0xd2, 0xbd, 0x4e, 0x3e, 0x90, 0x93, 0x92, 0x92, +0xe0, 0xf1, 0xff, 0xf6, 0x73, 0x60, 0x73, 0x75, +0x7f, 0x66, 0x7e, 0x5b, 0xd7, 0x56, 0xd5, 0xd2, +0xdf, 0xd2, 0xd0, 0x48, 0x78, 0x65, 0x66, 0x6f, +0x6b, 0x6c, 0x6f, 0x68, 0x6b, 0x6e, 0x6c, 0x62, +0x65, 0x78, 0x7d, 0x48, 0x4b, 0x4a, 0x42, 0xda, +0xf4, 0xc0, 0xcf, 0xfd, 0xe6, 0xe0, 0xe3, 0xe9, +0x91, 0x92, 0x9c, 0x9f, 0x90, 0xe2, 0xe7, 0xfa, +0xc3, 0x46, 0x75, 0x5f, 0x47, 0x42, 0x48, 0x44, +0xd3, 0xd0, 0xc5, 0xdf, 0xd2, 0xd8, 0xd8, 0x58, +0x7c, 0x72, 0x73, 0x7e, 0x60, 0x61, 0x65, 0x67, +0x60, 0x6d, 0x66, 0x7c, 0x71, 0x7d, 0x7d, 0x4a, +0xd4, 0xd8, 0xd2, 0xc1, 0xf5, 0xce, 0xdb, 0xf0, +0xe6, 0xe6, 0xe6, 0xed, 0xea, 0x97, 0x91, 0x92, +0x9c, 0x97, 0xe4, 0xfb, 0xe4, 0xc1, 0x74, 0x40, +0x54, 0x50, 0x4e, 0x75, 0x45, 0x54, 0x50, 0x44, +0x5f, 0x5d, 0x5b, 0x46, 0x73, 0x64, 0x7a, 0x7e, +0x67, 0x63, 0x66, 0x60, 0x6c, 0x69, 0x6c, 0x60, +0x64, 0x7a, 0x7b, 0x7c, 0x48, 0x45, 0x5b, 0x59, +0xd8, 0xca, 0xcc, 0xc3, 0xf0, 0xfa, 0xe5, 0xe1, +0xed, 0xea, 0x96, 0x91 }; +char packet_9[] = { +0x80, 0x08, 0x2c, 0x46, 0x11, 0x47, 0x33, 0x82, +0x58, 0xf3, 0x3d, 0xea, 0x5b, 0x47, 0x55, 0xdf, +0xd1, 0xd5, 0x54, 0xd3, 0xc4, 0x55, 0x5e, 0x56, +0xd1, 0xd2, 0xde, 0xdd, 0x54, 0x5c, 0x45, 0x5d, +0x55, 0x5d, 0x59, 0xd6, 0x55, 0x5a, 0xd3, 0xd9, +0xd0, 0xdc, 0xd8, 0xd8, 0xc6, 0xca, 0xc6, 0xd1, +0x5a, 0x7f, 0x7e, 0x4f, 0xdd, 0xd9, 0x57, 0x45, +0x59, 0xd7, 0xdd, 0xc3, 0xc6, 0x54, 0x5c, 0xd4, +0xdd, 0xdd, 0x5d, 0x43, 0x55, 0xdf, 0xda, 0xcc, +0xc9, 0x54, 0x4f, 0x5c, 0x55, 0xd2, 0xd0, 0xd1, +0x52, 0x5f, 0xdd, 0x53, 0x58, 0x53, 0xd6, 0xdd, +0xd9, 0xdc, 0xc1, 0xd7, 0x4a, 0x4e, 0x47, 0x51, +0xdf, 0xf5, 0xc6, 0x57, 0x5b, 0x75, 0x74, 0x55, +0xf6, 0xdb, 0x5e, 0x50, 0xd4, 0xd3, 0x56, 0x58, +0x5f, 0xd7, 0xdd, 0xde, 0xd2, 0xd5, 0x4d, 0x71, +0x5f, 0xf4, 0xf2, 0xc0, 0x50, 0x41, 0x5c, 0xd0, +0xd7, 0xdc, 0xd1, 0x45, 0x4e, 0x5c, 0x54, 0x5e, +0x77, 0x72, 0x5a, 0xcf, 0xf2, 0xf4, 0xda, 0xd2, +0xd6, 0x58, 0x5e, 0xdf, 0xc1, 0xc2, 0x57, 0x40, +0x56, 0xd7, 0xd7, 0xd5, 0x56, 0x45, 0x43, 0x50, +0xd9, 0xd2, 0x52, 0x5e, 0x5d, 0xd2, 0xd8, 0xd2, +0x54, 0x58, 0x41, 0x43 }; +char packet_10[] = { +0x80, 0x08, 0x2c, 0x47, 0x11, 0x47, 0x34, 0x22, +0x58, 0xf3, 0x3d, 0xea, 0x56, 0xd4, 0xd6, 0x58, +0x42, 0x53, 0xc4, 0xf7, 0xcb, 0xf5, 0xf4, 0xc1, +0xd0, 0x4d, 0x45, 0xd6, 0x57, 0xd0, 0xd9, 0xdb, +0x50, 0x44, 0x59, 0x4b, 0x4f, 0xd6, 0xd8, 0x57, +0x5e, 0xd6, 0x57, 0xd0, 0xc5, 0xdf, 0xc6, 0xcd, +0xc5, 0x51, 0x50, 0xd0, 0xd6, 0xd2, 0xdb, 0x5d, +0x75, 0x75, 0x73, 0x75, 0xc0, 0xf7, 0xf0, 0xf6, +0xc9, 0xd8, 0xd5, 0x57, 0x56, 0x48, 0x70, 0x75, +0x42, 0x42, 0x5f, 0xd0, 0x5d, 0x5b, 0x5c, 0x55, +0xd6, 0xc1, 0xcf, 0xd8, 0xd3, 0xd0, 0x45, 0x4f, +0x54, 0xc6, 0xd0, 0x59, 0x55, 0xc4, 0xd1, 0x5a, +0x73, 0x7f, 0x47, 0x54, 0xd1, 0xdf, 0xdc, 0xd7, +0xc7, 0xc8, 0xdb, 0xc7, 0xca, 0xc9, 0xc0, 0xc7, +0xc1, 0x51, 0x40, 0x5f, 0x52, 0xd7, 0xda, 0xdf, +0x41, 0x4c, 0x56, 0x54, 0x5f, 0x4a, 0x4a, 0x4e, +0x5d, 0xc4, 0xc6, 0xcd, 0xc7, 0xd6, 0x52, 0xd4, +0xc4, 0xc4, 0xc6, 0xd5, 0x5b, 0x41, 0x4e, 0x56, +0xc4, 0xd8, 0x54, 0x5e, 0x4d, 0x48, 0x76, 0x73, +0x4d, 0x5b, 0x59, 0xd8, 0xf0, 0xf7, 0xde, 0x52, +0xd0, 0xc3, 0xfd, 0xfa, 0xfd, 0xc1, 0x40, 0x4a, +0x5a, 0x45, 0x5d, 0xd0 }; + +#define MAKE_TEST(n) \ + /* deserialize the packet*/ \ + if((packet = trtp_rtp_packet_deserialize(packet_##n, sizeof(packet_##n)))){ \ + /* serialize the packet */ \ + if((buffer = trtp_rtp_packet_serialize(packet))){ \ + /* compare data */ \ + if(sizeof(packet_##n) != buffer->size){ \ + TSK_DEBUG_ERROR("Test-%d: Sizes are different", n); \ + } \ + else{ \ + for(i=0; i<buffer->size; i++){ \ + if(packet_##n[i] != ((char*)buffer->data)[i]){ \ + TSK_DEBUG_ERROR("Test-%d: Data is different", n); \ + break; \ + } \ + } \ + if(i==buffer->size){ \ + TSK_DEBUG_INFO("Test-%d: OK", n); \ + } \ + } \ + } \ + else{ \ + TSK_DEBUG_ERROR("Failed to serialize packet-%d", n); \ + } \ + \ + TSK_OBJECT_SAFE_FREE(buffer); \ + TSK_OBJECT_SAFE_FREE(packet); \ + } \ + else{ \ + TSK_DEBUG_ERROR("Failed to deserialize packet-%d", n); \ + } + +void test_parser() +{ + trtp_rtp_packet_t* packet; + tsk_buffer_t* buffer; + tsk_size_t i; + + MAKE_TEST(0); + MAKE_TEST(1); + MAKE_TEST(2); + MAKE_TEST(3); + MAKE_TEST(4); + MAKE_TEST(5); + MAKE_TEST(6); + MAKE_TEST(7); + MAKE_TEST(8); + MAKE_TEST(9); + MAKE_TEST(10); +} + + +#endif /* _TEST_PARSER_H_ */ diff --git a/tinyRTP/tinyRTP.pc.in b/tinyRTP/tinyRTP.pc.in new file mode 100644 index 0000000..621003d --- /dev/null +++ b/tinyRTP/tinyRTP.pc.in @@ -0,0 +1,14 @@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +libdir = @libdir@ +includedir = @includedir@ + +Name : libtinyRTP +Description : Doubango Telecom tinyRTP ((S)RTP/(S)RTCP protocols) library +Version : @PACKAGE_VERSION@ +Requires: +Requires.private: tinySAK = @PACKAGE_VERSION@ tinyNET = @PACKAGE_VERSION@ tinyMEDIA = @PACKAGE_VERSION@ +Conflicts: +Cflags : -I${includedir}/tinyrtp +Libs : -L${libdir} -ltinyRTP +Libs.private: diff --git a/tinyRTP/tinyRTP.sln b/tinyRTP/tinyRTP.sln new file mode 100644 index 0000000..ec6e279 --- /dev/null +++ b/tinyRTP/tinyRTP.sln @@ -0,0 +1,92 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyRTP", "tinyRTP.vcproj", "{99B7D02F-8C70-4B45-AF3C-92313C3CEE15}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinySAK", "..\tinySAK\tinySAK.vcproj", "{6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyNET", "..\tinyNET\tinyNET.vcproj", "{7522A458-92F4-4259-B906-E84C2A65D9F1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcproj", "{A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + Release|Win32 = Release|Win32 + Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + Static_Debug|Win32 = Static_Debug|Win32 + Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) = Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + Static_Release|Win32 = Static_Release|Win32 + Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) = Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Debug|Win32.ActiveCfg = Debug|Win32 + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Debug|Win32.Build.0 = Debug|Win32 + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32 + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Release|Win32.ActiveCfg = Release|Win32 + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Release|Win32.Build.0 = Release|Win32 + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32 + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Debug|Win32.ActiveCfg = Debug|Win32 + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Debug|Win32.Build.0 = Debug|Win32 + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32 + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Release|Win32.ActiveCfg = Release|Win32 + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Release|Win32.Build.0 = Release|Win32 + {99B7D02F-8C70-4B45-AF3C-92313C3CEE15}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32 + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Debug|Win32.ActiveCfg = Debug|Win32 + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Debug|Win32.Build.0 = Debug|Win32 + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Release|Win32.ActiveCfg = Release|Win32 + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Release|Win32.Build.0 = Release|Win32 + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Debug|Win32.ActiveCfg = Static_Debug|Win32 + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Debug|Win32.Build.0 = Static_Debug|Win32 + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Release|Win32.ActiveCfg = Static_Release|Win32 + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Release|Win32.Build.0 = Static_Release|Win32 + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {6BC9B796-10C6-4CF7-A6E4-E2DACCDA84DA}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Debug|Win32.ActiveCfg = Debug|Win32 + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Debug|Win32.Build.0 = Debug|Win32 + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Release|Win32.ActiveCfg = Release|Win32 + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Release|Win32.Build.0 = Release|Win32 + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Debug|Win32.ActiveCfg = Debug|Win32 + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Debug|Win32.Build.0 = Debug|Win32 + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Release|Win32.ActiveCfg = Release|Win32 + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Release|Win32.Build.0 = Release|Win32 + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Build.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {7522A458-92F4-4259-B906-E84C2A65D9F1}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I) + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Debug|Win32.ActiveCfg = Debug|Win32 + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Debug|Win32.Build.0 = Debug|Win32 + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32 + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Release|Win32.ActiveCfg = Release|Win32 + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Release|Win32.Build.0 = Release|Win32 + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32 + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Static_Debug|Win32.ActiveCfg = Debug|Win32 + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Static_Debug|Win32.Build.0 = Debug|Win32 + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Static_Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Win32 + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Static_Release|Win32.ActiveCfg = Release|Win32 + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Static_Release|Win32.Build.0 = Release|Win32 + {A6C1CDCE-63E1-4B60-AC4C-EF0410ED4432}.Static_Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tinyRTP/tinyRTP.vcproj b/tinyRTP/tinyRTP.vcproj new file mode 100644 index 0000000..794128b --- /dev/null +++ b/tinyRTP/tinyRTP.vcproj @@ -0,0 +1,384 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9.00" + Name="tinyRTP" + ProjectGUID="{99B7D02F-8C70-4B45-AF3C-92313C3CEE15}" + RootNamespace="tinyRTP" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="2" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="include;..\tinySAK\src;..\tinyNET\src;..\tinyMEDIA\include;..\thirdparties\win32\include" + PreprocessorDefinitions="DEBUG_LEVEL=DEBUG_LEVEL_INFO;HAVE_SRTP=1;WIN32;_DEBUG;_WINDOWS;_USRDLL;TINYRTP_EXPORTS" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + WarnAsError="true" + DebugInformationFormat="4" + CompileAs="1" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib $(OutDir)\tinySAK.lib $(OutDir)\tinyNET.lib $(OutDir)\tinyMEDIA.lib "..\thirdparties\win32\lib\srtp\libsrtp.a" ..\thirdparties\win32\lib\libgcc.a" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="2" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="3" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="..\thirdparties\win32\include;include;..\tinySAK\src;..\tinyNET\src;..\tinyMEDIA\include" + PreprocessorDefinitions="HAVE_SRTP=1;WIN32;NDEBUG;_WINDOWS;_USRDLL;TINYRTP_EXPORTS" + RuntimeLibrary="2" + EnableFunctionLevelLinking="false" + UsePrecompiledHeader="0" + WarningLevel="3" + WarnAsError="true" + DebugInformationFormat="0" + CompileAs="1" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="ws2_32.lib $(OutDir)\tinySAK.lib $(OutDir)\tinyNET.lib $(OutDir)\tinyMEDIA.lib "..\thirdparties\win32\lib\srtp\libsrtp.a" "..\thirdparties\win32\lib\libgcc.a"" + LinkIncremental="1" + IgnoreDefaultLibraryNames="MSVCRTD" + GenerateDebugInformation="false" + SubSystem="2" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="0" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="source(*.c)" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\src\trtp.c" + > + </File> + <File + RelativePath=".\src\trtp_manager.c" + > + </File> + <File + RelativePath=".\src\trtp_srtp.c" + > + </File> + <Filter + Name="rtp" + > + <File + RelativePath=".\src\rtp\trtp_rtp_header.c" + > + </File> + <File + RelativePath=".\src\rtp\trtp_rtp_packet.c" + > + </File> + <File + RelativePath=".\src\rtp\trtp_rtp_session.c" + > + </File> + </Filter> + <Filter + Name="rtcp" + > + <File + RelativePath=".\src\rtcp\trtp_rtcp_header.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_packet.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_rblock.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_report.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_report_bye.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_report_fb.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_report_rr.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_report_sdes.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_report_sr.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_report_xr.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_sdes_chunck.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_sdes_item.c" + > + </File> + <File + RelativePath=".\src\rtcp\trtp_rtcp_session.c" + > + </File> + </Filter> + <Filter + Name="rtsp" + > + </Filter> + </Filter> + <Filter + Name="include(*.h)" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath=".\include\tinyrtp.h" + > + </File> + <File + RelativePath=".\include\tinyrtp_config.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\trtp.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\trtp_manager.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\trtp_srtp.h" + > + </File> + <Filter + Name="rtp" + > + <File + RelativePath=".\include\tinyrtp\rtp\trtp_rtp_header.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtp\trtp_rtp_packet.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtp\trtp_rtp_session.h" + > + </File> + </Filter> + <Filter + Name="rtcp" + > + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_header.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_packet.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_rblock.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_report.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_report_bye.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_report_fb.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_report_rr.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_report_sdes.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_report_sr.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_report_xr.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_sdes_chunck.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_sdes_item.h" + > + </File> + <File + RelativePath=".\include\tinyrtp\rtcp\trtp_rtcp_session.h" + > + </File> + </Filter> + <Filter + Name="rtsp" + > + </Filter> + </Filter> + <Filter + Name="resources(*.rc)" + > + <File + RelativePath=".\version.rc" + > + </File> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/tinyRTP/version.rc b/tinyRTP/version.rc new file mode 100644 index 0000000..9592d2e --- /dev/null +++ b/tinyRTP/version.rc @@ -0,0 +1,102 @@ +// Microsoft Visual C++ generated resource script. +// +// #include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2.0.0.1156 + PRODUCTVERSION 2.0.0.1156 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Doubango telecom" + VALUE "FileDescription", "Doubango IMS Framework" + VALUE "FileVersion", "2.0.0.1156" + VALUE "InternalName", "tinyrtp.dll" + VALUE "LegalCopyright", "(c) 2010-2013 Doubango Telecom. All rights reserved." + VALUE "OriginalFilename", "tinyrtp.dll" + VALUE "ProductName", "Doubango IMS Framework" + VALUE "ProductVersion", "2.0.0.1156" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + |