From d68fcb0690911b72964dcb4adcd168755b47c1f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joni=20R=C3=A4s=C3=A4nen?= Date: Tue, 6 Sep 2022 10:45:19 +0300 Subject: [PATCH] common: Add possibility only send or only receive The API forced uvgRTP to both bind to local port even if user does not need or want this and it also expects the user to know the remote remote address which is not always possible. This commit adds API to session for specifying only one port when creating media_stream. This commit also adds RCE_SEND_ONLY and RCE_RECEIVE_ONLY flags which can be used to prevent binding and specify the meaning of single address and port parameters. --- include/uvgrtp/context.hh | 2 +- include/uvgrtp/media_stream.hh | 27 +---- include/uvgrtp/rtcp.hh | 2 +- include/uvgrtp/session.hh | 60 +++++++--- include/uvgrtp/util.hh | 51 +++++---- src/context.cc | 14 ++- src/media_stream.cc | 200 +++++++++++++++++---------------- src/rtcp.cc | 24 +++- src/session.cc | 92 ++++++++++++--- 9 files changed, 283 insertions(+), 189 deletions(-) diff --git a/include/uvgrtp/context.hh b/include/uvgrtp/context.hh index a065f5e3..8592da0c 100644 --- a/include/uvgrtp/context.hh +++ b/include/uvgrtp/context.hh @@ -39,7 +39,7 @@ namespace uvgrtp { * \retval nullptr If "remote_addr" is empty * \retval nullptr If memory allocation failed */ - uvgrtp::session *create_session(std::string remote_addr); + uvgrtp::session *create_session(std::string address); /** * \brief Create a new RTP session diff --git a/include/uvgrtp/media_stream.hh b/include/uvgrtp/media_stream.hh index 28006bb5..3a68be69 100644 --- a/include/uvgrtp/media_stream.hh +++ b/include/uvgrtp/media_stream.hh @@ -40,7 +40,6 @@ namespace uvgrtp { class media_stream { public: /// \cond DO_NOT_DOCUMENT - media_stream(std::string cname, std::string addr, uint16_t src_port, uint16_t dst_port, rtp_format_t fmt, int rce_flags); media_stream(std::string cname, std::string remote_addr, std::string local_addr, uint16_t src_port, uint16_t dst_port, rtp_format_t fmt, int rce_flags); ~media_stream(); @@ -240,29 +239,6 @@ namespace uvgrtp { * \retval RTP_INVALID_VALUE If hook is nullptr */ rtp_error_t install_receive_hook(void *arg, void (*hook)(void *, uvgrtp::frame::rtp_frame *)); - /// \cond DO_NOT_DOCUMENT - /* - * - * Return RTP_OK on success - * Return RTP_INVALID_VALUE if "hook" is nullptr */ - rtp_error_t install_deallocation_hook(void (*hook)(void *)); - - /* If needed, a notification hook can be installed to uvgRTP that can be used as - * an information side channel to the internal state of the library. - * - * When uvgRTP encouters a situation it doesn't know how to react to, - * it calls the notify hook with certain notify reason number (src/util.hh). - * Upon receiving a notification, application may ignore it or act on it somehow - * - * Currently only one notification type is supported and only receiver uses notifications - * - * "arg" is optional argument that is passed to hook when it is called. It may be nullptr - * - * Return RTP_OK on success - * Return RTP_INVALID_VALUE if "hook" is nullptr */ - rtp_error_t install_notify_hook(void *arg, void (*hook)(void *, int)); - /// \endcond - /** * \brief Configure the media stream, see ::RTP_CTX_CONFIGURATION_FLAGS for more details * @@ -320,6 +296,9 @@ namespace uvgrtp { uint32_t get_default_bandwidth_kbps(rtp_format_t fmt); + bool check_pull_preconditions(); + rtp_error_t check_push_preconditions(); + uint32_t key_; std::shared_ptr srtp_; diff --git a/include/uvgrtp/rtcp.hh b/include/uvgrtp/rtcp.hh index 24def2df..3b0bcc57 100644 --- a/include/uvgrtp/rtcp.hh +++ b/include/uvgrtp/rtcp.hh @@ -186,7 +186,7 @@ namespace uvgrtp { * (or whether we're even sending anything) * * Return RTP_OK on success and RTP_ERROR on error */ - rtp_error_t add_participant(std::string dst_addr, uint16_t dst_port, uint16_t src_port, uint32_t clock_rate); + rtp_error_t add_participant(std::string src_addr, std::string dst_addr, uint16_t dst_port, uint16_t src_port, uint32_t clock_rate); /* Functions for updating various RTP sender statistics */ void sender_update_stats(const uvgrtp::frame::rtp_frame *frame); diff --git a/include/uvgrtp/session.hh b/include/uvgrtp/session.hh index 6525b740..9074c9ca 100644 --- a/include/uvgrtp/session.hh +++ b/include/uvgrtp/session.hh @@ -21,8 +21,7 @@ namespace uvgrtp { public: /// \cond DO_NOT_DOCUMENT session(std::string cname, std::string addr); - session(std::string cname, std::string remote_addr, - std::string local_addr); + session(std::string cname, std::string remote_addr, std::string local_addr); ~session(); /// \endcond @@ -31,33 +30,60 @@ namespace uvgrtp { * * \details * - * If local_addr was provided when uvgrtp::session was created, uvgRTP binds - * itself to local_addr:src_port, otherwise to INADDR_ANY:src_port + * If both addresses were provided when uvgrtp::session was created, uvgRTP binds + * itself to local_addr:src_port and sends packets to remote_addr:dst_port. + * + * If only one address was provided, the RCE_SEND_ONLY flag in rce_flags can be used to + * avoid binding and src_port is thus ignored. RCE_RECEIVE_ONLY means dst_port is ignored. + * Without either, the one address is interpreted as remote_addr and binding happens to ANY. * * This object is used for both sending and receiving media, see documentation * for uvgrtp::media_stream for more details. * - * User can enable and disable functionality of uvgRTP by OR'ing RCE_* flags + * User can enable and disable functionality of uvgRTP by OR'ing (using |) RCE_* flags * together and passing them using the rce_flags parameter * - * \param src_port Local port that uvgRTP listens to for incoming RTP packets - * \param dst_port Remote port where uvgRTP sends RTP packets - * \param fmt Format of the media stream. see ::RTP_FORMAT for more details - * \param rce_flags RTP context enable flags, see ::RTP_CTX_ENABLE_FLAGS for more details + * \param src_port Local port that uvgRTP listens to for incoming RTP packets + * \param dst_port Remote port where uvgRTP sends RTP packets + * \param fmt Format of the media stream. see ::RTP_FORMAT for more details + * \param rce_flags RTP context enable flags, see ::RTP_CTX_ENABLE_FLAGS for more details * * \return RTP media stream object * * \retval uvgrtp::media_stream* On success - * \retval nullptr If src_port or dst_port is 0 - * \retval nullptr If fmt is not a supported media format - * \retval nullptr If socket initialization failed - * \retval nullptr If ZRTP was enabled and it failed to finish handshaking - * \retval nullptr If RCE_SRTP is given but uvgRTP has not been compiled with Crypto++ enabled - * \retval nullptr If RCE_SRTP is given but RCE_SRTP_KMNGMNT_* flag is not given - * \retval nullptr If memory allocation failed + * \retval nullptr On failure, see print and */ uvgrtp::media_stream *create_stream(uint16_t src_port, uint16_t dst_port, rtp_format_t fmt, int rce_flags); + /** + * \brief Create a bidirectional media stream for an RTP session + * + * \details + * + * If both addresses were provided when uvgrtp::session was created, uvgRTP binds + * sends packets to remote_addr:port and does not bind. + * + * If only one address was provided, the RCE_SEND_ONLY flag in rce_flags can be used to + * avoid binding and port is used as remote_port. RCE_RECEIVE_ONLY means port is used for binding. + * Without either, the one address is interpreted as remote_addr and binding happens to ANY. + * + * This object is used for both sending and receiving media, see documentation + * for uvgrtp::media_stream for more details. + * + * User can enable and disable functionality of uvgRTP by OR'ing (using |) RCE_* flags + * together and passing them using the rce_flags parameter + * + * \param port Either local or remote port depending on rce_flags + * \param fmt Format of the media stream. see ::RTP_FORMAT for more details + * \param rce_flags RTP context enable flags, see ::RTP_CTX_ENABLE_FLAGS for more details + * + * \return RTP media stream object + * + * \retval uvgrtp::media_stream* On success + * \retval nullptr On failure, see print + */ + uvgrtp::media_stream *create_stream(uint16_t port, rtp_format_t fmt, int rce_flags); + /** * \brief Destroy a media stream * @@ -81,6 +107,8 @@ namespace uvgrtp { /* Each RTP multimedia session shall have one ZRTP session from which all session are derived */ std::shared_ptr zrtp_; + std::string generic_address_; + /* Each RTP multimedia session is always IP-specific */ std::string remote_address_; diff --git a/include/uvgrtp/util.hh b/include/uvgrtp/util.hh index d8490d5c..ee01919f 100644 --- a/include/uvgrtp/util.hh +++ b/include/uvgrtp/util.hh @@ -187,14 +187,19 @@ typedef enum RTP_FLAGS { enum RTP_CTX_ENABLE_FLAGS { RCE_NO_FLAGS = 0, - // Obsolete flags - RCE_SYSTEM_CALL_DISPATCHER = 0, - RCE_NO_H26X_INTRA_DELAY = 0, - RCE_NO_H26X_SCL = 0, - RCE_H26X_NO_DEPENDENCY_ENFORCEMENT = 0, + // Obsolete flags, they do nothing because the feature has been removed or they are enabled by default + RCE_OBSOLETE = 1, // for checking if user inputs obsolete flags + RCE_SYSTEM_CALL_DISPATCHER = 1, // removed feature + RCE_NO_H26X_INTRA_DELAY = 1, // removed feature + RCE_NO_H26X_SCL = 1, // this flag was moved to be an RTP flag + RCE_H26X_NO_DEPENDENCY_ENFORCEMENT = 1, // the feature is disabled by default + + // These can be used to specify what the address does for one address create session + RCE_SEND_ONLY = 1 << 1, // address interpreted as remote, no binding to socket + RCE_RECEIVE_ONLY = 1 << 2, // address interpreted as local, sending not possible /** Use SRTP for this connection */ - RCE_SRTP = 1, + RCE_SRTP = 1 << 3, /** Use ZRTP for key management * @@ -204,7 +209,7 @@ enum RTP_CTX_ENABLE_FLAGS { * * This flag must be coupled with RCE_SRTP and is mutually exclusive * with RCE_SRTP_KMNGMNT_USER. */ - RCE_SRTP_KMNGMNT_ZRTP = 1 << 1, + RCE_SRTP_KMNGMNT_ZRTP = 1 << 4, /** Use user-defined way to manage keys * @@ -214,24 +219,24 @@ enum RTP_CTX_ENABLE_FLAGS { * * This flag must be coupled with RCE_SRTP and is mutually exclusive * with RCE_SRTP_KMNGMNT_ZRTP */ - RCE_SRTP_KMNGMNT_USER = 1 << 2, + RCE_SRTP_KMNGMNT_USER = 1 << 5, /** By default, the RTP packet payload does not include the start code prefixes. * Use this flag to prepend the 4-byte start code (0x00000001) to each received * H26x frame, so there is no difference with sender input. Recommended in * most cases. */ - RCE_H26X_PREPEND_SC = 1 << 3, + RCE_H26X_PREPEND_SC = 1 << 6, /** Use this flag to discard inter frames that don't have their previous dependencies arrived. Does not work if the dependencies are not in monotonic order. */ - RCE_H26X_DEPENDENCY_ENFORCEMENT = 1 << 4, + RCE_H26X_DEPENDENCY_ENFORCEMENT = 1 << 7, /** Fragment frames into RTP packets of MTU size (1500 bytes). * * Some RTP profiles define fragmentation with marker bit indicating the end of frame. * You can enable this functionality using this flag at both sender and receiver. */ - RCE_FRAGMENT_GENERIC = 1 << 5, + RCE_FRAGMENT_GENERIC = 1 << 8, /** If SRTP is enabled and RCE_INPLACE_ENCRYPTION flag is *not* given, * uvgRTP will make a copy of the frame given to push_frame(). @@ -241,13 +246,13 @@ enum RTP_CTX_ENABLE_FLAGS { * unnecessary copy operations. * * If RCE_INPLACE_ENCRYPTION is given to push_frame(), the input pointer must be writable! */ - RCE_SRTP_INPLACE_ENCRYPTION = 1 << 6, + RCE_SRTP_INPLACE_ENCRYPTION = 1 << 9, /** Disable System Call Clustering (SCC) */ - RCE_NO_SYSTEM_CALL_CLUSTERING = 1 << 7, + RCE_NO_SYSTEM_CALL_CLUSTERING = 1 << 10, /** Disable RTP payload encryption */ - RCE_SRTP_NULL_CIPHER = 1 << 8, + RCE_SRTP_NULL_CIPHER = 1 << 11, /** Enable RTP packet authentication * @@ -255,31 +260,31 @@ enum RTP_CTX_ENABLE_FLAGS { * to each outgoing RTP packet for all streams that have SRTP enabled. * * NOTE: this flag must be coupled with at least RCE_SRTP */ - RCE_SRTP_AUTHENTICATE_RTP = 1 << 9, + RCE_SRTP_AUTHENTICATE_RTP = 1 << 12, /** Enable packet replay protection */ - RCE_SRTP_REPLAY_PROTECTION = 1 << 10, + RCE_SRTP_REPLAY_PROTECTION = 1 << 13, /** Enable RTCP for the media stream. * If SRTP is enabled, SRTCP is used instead */ - RCE_RTCP = 1 << 11, + RCE_RTCP = 1 << 14, /** If the Mediastream object is used as a unidirectional stream * but holepunching has been enabled, this flag can be used to make * uvgRTP periodically send a short UDP datagram to keep the hole * in the firewall open */ - RCE_HOLEPUNCH_KEEPALIVE = 1 << 12, + RCE_HOLEPUNCH_KEEPALIVE = 1 << 15, /** Use 192-bit keys with SRTP */ - RCE_SRTP_KEYSIZE_192 = 1 << 13, + RCE_SRTP_KEYSIZE_192 = 1 << 16, /** Use 256-bit keys with SRTP */ - RCE_SRTP_KEYSIZE_256 = 1 << 14, + RCE_SRTP_KEYSIZE_256 = 1 << 17, - RCE_ZRTP_MULTISTREAM_NO_DH = 1 << 16, + RCE_ZRTP_MULTISTREAM_NO_DH = 1 << 18, - RCE_LAST = 1 << 17, -}; + RCE_LAST = 1 << 19 +}; // maximum is 1 << 30 for int /** diff --git a/src/context.cc b/src/context.cc index a0a93ccd..db2ca824 100644 --- a/src/context.cc +++ b/src/context.cc @@ -38,18 +38,24 @@ uvgrtp::context::~context() #endif } -uvgrtp::session *uvgrtp::context::create_session(std::string remote_addr) +uvgrtp::session *uvgrtp::context::create_session(std::string address) { - if (remote_addr == "") + if (address == "") + { + UVG_LOG_ERROR("Please specify the address you want to communicate with!"); return nullptr; + } - return new uvgrtp::session(get_cname(), remote_addr); + return new uvgrtp::session(get_cname(), address); } uvgrtp::session *uvgrtp::context::create_session(std::string remote_addr, std::string local_addr) { - if (remote_addr == "" || local_addr == "") + if (remote_addr == "" && local_addr == "") + { + UVG_LOG_ERROR("Please specify at least one address for create_session"); return nullptr; + } return new uvgrtp::session(get_cname(), remote_addr, local_addr); } diff --git a/src/media_stream.cc b/src/media_stream.cc index b8383540..c4f591a7 100644 --- a/src/media_stream.cc +++ b/src/media_stream.cc @@ -20,42 +20,33 @@ #include #include -uvgrtp::media_stream::media_stream(std::string cname, std::string addr, - uint16_t src_port, uint16_t dst_port, rtp_format_t fmt, int rce_flags): +uvgrtp::media_stream::media_stream(std::string cname, std::string remote_addr, + std::string local_addr, uint16_t src_port, uint16_t dst_port, rtp_format_t fmt, + int rce_flags): + key_(uvgrtp::random::generate_32()), srtp_(nullptr), srtcp_(nullptr), socket_(nullptr), rtp_(nullptr), rtcp_(nullptr), - rce_flags_(), + remote_sockaddr_(), + remote_address_(remote_addr), + local_address_(local_addr), + src_port_(src_port), + dst_port_(dst_port), + fmt_(fmt), + rce_flags_(rce_flags), media_config_(nullptr), initialized_(false), rtp_handler_key_(0), + zrtp_handler_key_(0), reception_flow_(nullptr), media_(nullptr), holepuncher_(nullptr), cname_(cname), fps_enumerator_(30), fps_denominator_(1) -{ - fmt_ = fmt; - remote_address_ = addr; - local_address_ = ""; - rce_flags_ = rce_flags; - src_port_ = src_port; - dst_port_ = dst_port; - key_ = uvgrtp::random::generate_32(); -} - -uvgrtp::media_stream::media_stream(std::string cname, - std::string remote_addr, std::string local_addr, - uint16_t src_port, uint16_t dst_port, - rtp_format_t fmt, int rce_flags -): - media_stream(cname, remote_addr, src_port, dst_port, fmt, rce_flags) -{ - local_address_ = local_addr; -} +{} uvgrtp::media_stream::~media_stream() { @@ -100,27 +91,45 @@ rtp_error_t uvgrtp::media_stream::init_connection() short int family = AF_INET; - // no reason to fail sending even if binding fails so we set remote address first - remote_sockaddr_ = socket_->create_sockaddr(family, remote_address_, dst_port_); - socket_->set_sockaddr(remote_sockaddr_); + if (!(rce_flags_ & RCE_RECEIVE_ONLY) && remote_address_ != "") + { + // no reason to fail sending even if binding fails so we set remote address first + remote_sockaddr_ = socket_->create_sockaddr(family, remote_address_, dst_port_); + socket_->set_sockaddr(remote_sockaddr_); + } + else + { + UVG_LOG_INFO("Sending disabled for this stream"); + } - if (local_address_ != "") { - sockaddr_in bind_addr = socket_->create_sockaddr(family, local_address_, src_port_); + if (!(rce_flags_ & RCE_SEND_ONLY)) + { + if (local_address_ != "" && src_port_ != 0) { + sockaddr_in bind_addr = socket_->create_sockaddr(family, local_address_, src_port_); - if ((ret = socket_->bind(bind_addr)) != RTP_OK) + if ((ret = socket_->bind(bind_addr)) != RTP_OK) + { + log_platform_error("bind(2) failed"); + return ret; + } + } + else if (src_port_ != 0) { - log_platform_error("bind(2) failed"); - return ret; + if ((ret = socket_->bind(family, INADDR_ANY, src_port_)) != RTP_OK) + { + log_platform_error("bind(2) to any failed"); + return ret; + } } - } - else - { - if ((ret = socket_->bind(family, INADDR_ANY, src_port_)) != RTP_OK) + else { - log_platform_error("bind(2) to any failed"); - return ret; + UVG_LOG_INFO("Not binding, receiving is not possible"); } } + else + { + UVG_LOG_INFO("Not binding, receiving is not possible"); + } /* Set the default UDP send/recv buffer sizes to 4MB as on Windows * the default size is way too small for a larger video conference */ @@ -398,9 +407,19 @@ rtp_error_t uvgrtp::media_stream::start_components() } if (rce_flags_ & RCE_RTCP) { - rtcp_->add_participant(remote_address_, src_port_ + 1, dst_port_ + 1, rtp_->get_clock_rate()); - rtcp_->set_session_bandwidth(get_default_bandwidth_kbps(fmt_)); - rtcp_->start(); + + if (remote_address_ == "" || + src_port_ == 0 || + dst_port_ == 0) + { + UVG_LOG_ERROR("Using RTCP requires setting at least remote address, local port and remote port"); + } + else + { + rtcp_->add_participant(local_address_, remote_address_, src_port_ + 1, dst_port_ + 1, rtp_->get_clock_rate()); + rtcp_->set_session_bandwidth(get_default_bandwidth_kbps(fmt_)); + rtcp_->start(); + } } if (rce_flags_ & RCE_SRTP_AUTHENTICATE_RTP) @@ -412,73 +431,67 @@ rtp_error_t uvgrtp::media_stream::start_components() rtp_error_t uvgrtp::media_stream::push_frame(uint8_t *data, size_t data_len, int rtp_flags) { - if (!initialized_) { - UVG_LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); - return RTP_NOT_INITIALIZED; - } + rtp_error_t ret = check_push_preconditions(); + if (ret == RTP_OK) + { + if (rce_flags_ & RCE_HOLEPUNCH_KEEPALIVE && holepuncher_) + holepuncher_->notify(); - if (rce_flags_ & RCE_HOLEPUNCH_KEEPALIVE && holepuncher_) - holepuncher_->notify(); + ret = media_->push_frame(data, data_len, rtp_flags); + } - return media_->push_frame(data, data_len, rtp_flags); + return ret; } rtp_error_t uvgrtp::media_stream::push_frame(std::unique_ptr data, size_t data_len, int rtp_flags) { - if (!initialized_) { - UVG_LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); - return RTP_NOT_INITIALIZED; - } + rtp_error_t ret = check_push_preconditions(); + if (ret == RTP_OK) + { + if (rce_flags_ & RCE_HOLEPUNCH_KEEPALIVE && holepuncher_) + holepuncher_->notify(); - if (rce_flags_ & RCE_HOLEPUNCH_KEEPALIVE && holepuncher_) - holepuncher_->notify(); + ret = media_->push_frame(std::move(data), data_len, rtp_flags); + } - return media_->push_frame(std::move(data), data_len, rtp_flags); + return ret; } rtp_error_t uvgrtp::media_stream::push_frame(uint8_t *data, size_t data_len, uint32_t ts, int rtp_flags) { - rtp_error_t ret = RTP_GENERIC_ERROR; + rtp_error_t ret = check_push_preconditions(); + if (ret == RTP_OK) + { + if (rce_flags_ & RCE_HOLEPUNCH_KEEPALIVE) + holepuncher_->notify(); - if (!initialized_) { - UVG_LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); - return RTP_NOT_INITIALIZED; + rtp_->set_timestamp(ts); + ret = media_->push_frame(data, data_len, rtp_flags); + rtp_->set_timestamp(INVALID_TS); } - if (rce_flags_ & RCE_HOLEPUNCH_KEEPALIVE) - holepuncher_->notify(); - - rtp_->set_timestamp(ts); - ret = media_->push_frame(data, data_len, rtp_flags); - rtp_->set_timestamp(INVALID_TS); - return ret; } rtp_error_t uvgrtp::media_stream::push_frame(std::unique_ptr data, size_t data_len, uint32_t ts, int rtp_flags) { - rtp_error_t ret = RTP_GENERIC_ERROR; + rtp_error_t ret = check_push_preconditions(); + if (ret == RTP_OK) + { + if (rce_flags_ & RCE_HOLEPUNCH_KEEPALIVE) + holepuncher_->notify(); - if (!initialized_) { - UVG_LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); - return RTP_NOT_INITIALIZED; + rtp_->set_timestamp(ts); + ret = media_->push_frame(std::move(data), data_len, rtp_flags); + rtp_->set_timestamp(INVALID_TS); } - if (rce_flags_ & RCE_HOLEPUNCH_KEEPALIVE) - holepuncher_->notify(); - - rtp_->set_timestamp(ts); - ret = media_->push_frame(std::move(data), data_len, rtp_flags); - rtp_->set_timestamp(INVALID_TS); - return ret; } uvgrtp::frame::rtp_frame *uvgrtp::media_stream::pull_frame() { - if (!initialized_) { - UVG_LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); - rtp_errno = RTP_NOT_INITIALIZED; + if (!check_pull_preconditions()) { return nullptr; } @@ -487,49 +500,42 @@ uvgrtp::frame::rtp_frame *uvgrtp::media_stream::pull_frame() uvgrtp::frame::rtp_frame *uvgrtp::media_stream::pull_frame(size_t timeout_ms) { - if (!initialized_) { - UVG_LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); - rtp_errno = RTP_NOT_INITIALIZED; + if (!check_pull_preconditions()) { return nullptr; } return reception_flow_->pull_frame(timeout_ms); } -rtp_error_t uvgrtp::media_stream::install_receive_hook(void *arg, void (*hook)(void *, uvgrtp::frame::rtp_frame *)) +bool uvgrtp::media_stream::check_pull_preconditions() { if (!initialized_) { UVG_LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); - return RTP_NOT_INITIALIZED; + rtp_errno = RTP_NOT_INITIALIZED; + return false; } - if (!hook) - return RTP_INVALID_VALUE; - - reception_flow_->install_receive_hook(arg, hook); - - return RTP_OK; + return true; } -rtp_error_t uvgrtp::media_stream::install_deallocation_hook(void (*hook)(void *)) +rtp_error_t uvgrtp::media_stream::check_push_preconditions() { if (!initialized_) { UVG_LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); return RTP_NOT_INITIALIZED; } - if (!hook) + if (remote_address_ == "") + { + UVG_LOG_ERROR("Cannot push frame if remote address has not been provided!"); return RTP_INVALID_VALUE; - - /* TODO: */ + } return RTP_OK; } -rtp_error_t uvgrtp::media_stream::install_notify_hook(void *arg, void (*hook)(void *, int)) +rtp_error_t uvgrtp::media_stream::install_receive_hook(void *arg, void (*hook)(void *, uvgrtp::frame::rtp_frame *)) { - (void)arg, (void)hook; - if (!initialized_) { UVG_LOG_ERROR("RTP context has not been initialized fully, cannot continue!"); return RTP_NOT_INITIALIZED; @@ -538,7 +544,7 @@ rtp_error_t uvgrtp::media_stream::install_notify_hook(void *arg, void (*hook)(vo if (!hook) return RTP_INVALID_VALUE; - /* TODO: */ + reception_flow_->install_receive_hook(arg, hook); return RTP_OK; } diff --git a/src/rtcp.cc b/src/rtcp.cc index e53c91e6..25ee1d56 100644 --- a/src/rtcp.cc +++ b/src/rtcp.cc @@ -332,7 +332,7 @@ rtp_error_t uvgrtp::rtcp::set_sdes_items(const std::vectorsocket->bind(AF_INET, INADDR_ANY, src_port)) != RTP_OK) + if (src_addr != "") { - free_participant(std::move(p)); - return ret; + UVG_LOG_INFO("Binding RTCP to port %s:%d", src_addr.c_str(), src_port); + sockaddr_in bind_addr = p->socket->create_sockaddr(AF_INET, src_addr, src_port); + if ((ret = p->socket->bind(bind_addr)) != RTP_OK) + { + free_participant(std::move(p)); + return ret; + } + } + else + { + UVG_LOG_INFO("Binding RTCP to port %d (source port)", src_port); + if ((ret = p->socket->bind(AF_INET, INADDR_ANY, src_port)) != RTP_OK) + { + free_participant(std::move(p)); + return ret; + } } p->role = RECEIVER; diff --git a/src/session.cc b/src/session.cc index 0e3e3b4d..5844b0b1 100644 --- a/src/session.cc +++ b/src/session.cc @@ -7,21 +7,25 @@ #include "debug.hh" -uvgrtp::session::session(std::string cname, std::string addr): +uvgrtp::session::session(std::string cname, std::string addr) : #ifdef __RTP_CRYPTO__ zrtp_(new uvgrtp::zrtp()), #endif - remote_address_(addr), + generic_address_(addr), + remote_address_(""), local_address_(""), cname_(cname) -{ -} +{} uvgrtp::session::session(std::string cname, std::string remote_addr, std::string local_addr): - session(cname, remote_addr) -{ - local_address_ = local_addr; -} +#ifdef __RTP_CRYPTO__ + zrtp_(new uvgrtp::zrtp()), +#endif + generic_address_(""), + remote_address_(remote_addr), + local_address_(local_addr), + cname_(cname) +{} uvgrtp::session::~session() { @@ -31,23 +35,75 @@ uvgrtp::session::~session() streams_.clear(); } -uvgrtp::media_stream *uvgrtp::session::create_stream(uint16_t r_port, uint16_t s_port, rtp_format_t fmt, int rce_flags) +uvgrtp::media_stream* uvgrtp::session::create_stream(uint16_t port, rtp_format_t fmt, int rce_flags) +{ + if (rce_flags & RCE_RECEIVE_ONLY) + { + return create_stream(port, 0, fmt, rce_flags); + } + else if (rce_flags & RCE_SEND_ONLY) + { + return create_stream(0, port, fmt, rce_flags); + } + + UVG_LOG_WARN("You haven't specified the purpose of port with rce_flags. Using it as destination port and not binding"); + return create_stream(0, port, fmt, rce_flags); +} + +uvgrtp::media_stream* uvgrtp::session::create_stream(uint16_t src_port, uint16_t dst_port, rtp_format_t fmt, int rce_flags) { - if (rce_flags & RCE_SYSTEM_CALL_DISPATCHER) { - UVG_LOG_ERROR("SCD is no longer supported!"); + if (rce_flags & RCE_OBSOLETE) { + UVG_LOG_WARN("You are using a flag that has either been removed or has been enabled by default. Consider updating RCE flags"); + } + + if ((rce_flags & RCE_SEND_ONLY) && (rce_flags & RCE_RECEIVE_ONLY)) { + UVG_LOG_ERROR("Cannot both use RCE_SEND_ONLY and RCE_RECEIVE_ONLY!"); rtp_errno = RTP_NOT_SUPPORTED; return nullptr; } - uvgrtp::media_stream* stream = nullptr; + // select which address the one address we got as a parameter is + if (generic_address_ != "") + { + if (rce_flags & RCE_RECEIVE_ONLY) + { + local_address_ = generic_address_; + } + else + { + remote_address_ = generic_address_; + } + } + else if ((rce_flags & RCE_RECEIVE_ONLY) && local_address_ == "") + { + UVG_LOG_ERROR("RCE_RECEIVE_ONLY requires local address!"); + rtp_errno = RTP_INVALID_VALUE; + return nullptr; + } + else if ((rce_flags & RCE_SEND_ONLY) && remote_address_ == "") + { + UVG_LOG_ERROR("RCE_SEND_ONLY requires remote address!"); + rtp_errno = RTP_INVALID_VALUE; + return nullptr; + } - if (local_address_ == "") { - stream = new uvgrtp::media_stream(cname_, remote_address_, r_port, s_port, fmt, rce_flags); + if ((rce_flags & RCE_RECEIVE_ONLY) && src_port == 0) + { + UVG_LOG_ERROR("RCE_RECEIVE_ONLY requires source port!"); + rtp_errno = RTP_INVALID_VALUE; + return nullptr; } - else { - stream = new uvgrtp::media_stream(cname_, remote_address_, local_address_, r_port, s_port, fmt, rce_flags); + + if ((rce_flags & RCE_SEND_ONLY) && dst_port == 0) + { + UVG_LOG_ERROR("RCE_SEND_ONLY requires destination port!"); + rtp_errno = RTP_INVALID_VALUE; + return nullptr; } + uvgrtp::media_stream* stream = + new uvgrtp::media_stream(cname_, remote_address_, local_address_, src_port, dst_port, fmt, rce_flags); + if (rce_flags & RCE_SRTP) { if (!uvgrtp::crypto::enabled()) { UVG_LOG_ERROR("Recompile uvgRTP with -D__RTP_CRYPTO__"); @@ -74,7 +130,7 @@ uvgrtp::media_stream *uvgrtp::session::create_stream(uint16_t r_port, uint16_t s session_mtx_.unlock(); if (stream->init(zrtp_) != RTP_OK) { - UVG_LOG_ERROR("Failed to initialize media stream %s:%d/%d", remote_address_.c_str(), r_port, s_port); + UVG_LOG_ERROR("Failed to initialize media stream %s:%d/%d", remote_address_.c_str(), src_port, dst_port); delete stream; return nullptr; } @@ -88,7 +144,7 @@ uvgrtp::media_stream *uvgrtp::session::create_stream(uint16_t r_port, uint16_t s } } else { if (stream->init() != RTP_OK) { - UVG_LOG_ERROR("Failed to initialize media stream %s:%d/%d", remote_address_.c_str(), r_port, s_port); + UVG_LOG_ERROR("Failed to initialize media stream %s:%d/%d", remote_address_.c_str(), src_port, dst_port); delete stream; return nullptr; }